This commit is contained in:
7
react/features/base/lib-jitsi-meet/_.native.ts
Normal file
7
react/features/base/lib-jitsi-meet/_.native.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
// Re-export JitsiMeetJS from the library lib-jitsi-meet to (the other features
|
||||
// of) the project jitsi-meet.
|
||||
//
|
||||
|
||||
// @ts-ignore
|
||||
import JitsiMeetJS from 'lib-jitsi-meet';
|
||||
export { JitsiMeetJS as default };
|
||||
3
react/features/base/lib-jitsi-meet/_.web.ts
Normal file
3
react/features/base/lib-jitsi-meet/_.web.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
declare let JitsiMeetJS: any;
|
||||
|
||||
export default JitsiMeetJS;
|
||||
48
react/features/base/lib-jitsi-meet/actionTypes.ts
Normal file
48
react/features/base/lib-jitsi-meet/actionTypes.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* The type of Redux action which signals that {@link JitsiMeetJS} was disposed.
|
||||
*
|
||||
* {
|
||||
* type: LIB_DID_DISPOSE
|
||||
* }
|
||||
*/
|
||||
export const LIB_DID_DISPOSE = 'LIB_DID_DISPOSE';
|
||||
|
||||
/**
|
||||
* The type of Redux action which signals that {@link JitsiMeetJS.init()} was
|
||||
* invoked and completed successfully.
|
||||
*
|
||||
* {
|
||||
* type: LIB_DID_INIT
|
||||
* }
|
||||
*/
|
||||
export const LIB_DID_INIT = 'LIB_DID_INIT';
|
||||
|
||||
/**
|
||||
* Action to signal that lib-jitsi-meet initialized failed with error.
|
||||
*
|
||||
* {
|
||||
* type: LIB_INIT_ERROR,
|
||||
* error: Error
|
||||
* }
|
||||
*/
|
||||
export const LIB_INIT_ERROR = 'LIB_INIT_ERROR';
|
||||
|
||||
/**
|
||||
* The type of Redux action which signals that {@link JitsiMeetJS} will be
|
||||
* disposed.
|
||||
*
|
||||
* {
|
||||
* type: LIB_WILL_DISPOSE
|
||||
* }
|
||||
*/
|
||||
export const LIB_WILL_DISPOSE = 'LIB_WILL_DISPOSE';
|
||||
|
||||
/**
|
||||
* The type of Redux action which signals that {@link JitsiMeetJS.init()} will
|
||||
* be invoked.
|
||||
*
|
||||
* {
|
||||
* type: LIB_WILL_INIT
|
||||
* }
|
||||
*/
|
||||
export const LIB_WILL_INIT = 'LIB_WILL_INIT';
|
||||
84
react/features/base/lib-jitsi-meet/actions.ts
Normal file
84
react/features/base/lib-jitsi-meet/actions.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
// @ts-expect-error
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
import { isOnline } from '../net-info/selectors';
|
||||
|
||||
import JitsiMeetJS from './_';
|
||||
import {
|
||||
LIB_DID_DISPOSE,
|
||||
LIB_DID_INIT,
|
||||
LIB_INIT_ERROR,
|
||||
LIB_WILL_DISPOSE,
|
||||
LIB_WILL_INIT
|
||||
} from './actionTypes';
|
||||
import { isAnalyticsEnabled } from './functions.any';
|
||||
import logger from './logger';
|
||||
|
||||
/**
|
||||
* Disposes (of) lib-jitsi-meet.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function disposeLib() {
|
||||
return (dispatch: IStore['dispatch']) => {
|
||||
dispatch({ type: LIB_WILL_DISPOSE });
|
||||
|
||||
// TODO Currently, lib-jitsi-meet doesn't have the functionality to
|
||||
// dispose itself.
|
||||
dispatch({ type: LIB_DID_DISPOSE });
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes lib-jitsi-meet (i.e. {@link invokes JitsiMeetJS.init()}) with the
|
||||
* current config(uration).
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function initLib() {
|
||||
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
|
||||
const state = getState();
|
||||
const config = state['features/base/config'];
|
||||
|
||||
if (!config) {
|
||||
throw new Error('Cannot init lib-jitsi-meet without config');
|
||||
}
|
||||
|
||||
dispatch({ type: LIB_WILL_INIT });
|
||||
|
||||
try {
|
||||
JitsiMeetJS.init({
|
||||
enableAnalyticsLogging: isAnalyticsEnabled(getState),
|
||||
...config,
|
||||
externalStorage: jitsiLocalStorage.isLocalStorageDisabled() ? jitsiLocalStorage : undefined
|
||||
});
|
||||
JitsiMeetJS.setNetworkInfo({
|
||||
isOnline: isOnline(state)
|
||||
});
|
||||
|
||||
logger.info(`lib-jitsi-meet version: ${JitsiMeetJS.version}`);
|
||||
logger.info(`User Agent: ${navigator.userAgent}`);
|
||||
|
||||
dispatch({ type: LIB_DID_INIT });
|
||||
} catch (error: any) {
|
||||
dispatch(libInitError(error));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies about a specific error raised by {@link JitsiMeetJS.init()}.
|
||||
*
|
||||
* @param {Error} error - The Error raised by JitsiMeetJS.init().
|
||||
* @returns {{
|
||||
* type: LIB_INIT_ERROR,
|
||||
* error: Error
|
||||
* }}
|
||||
*/
|
||||
export function libInitError(error: Error) {
|
||||
return {
|
||||
type: LIB_INIT_ERROR,
|
||||
error
|
||||
};
|
||||
}
|
||||
99
react/features/base/lib-jitsi-meet/functions.any.ts
Normal file
99
react/features/base/lib-jitsi-meet/functions.any.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { IStateful } from '../app/types';
|
||||
import { ConnectionFailedError } from '../connection/types';
|
||||
import { toState } from '../redux/functions';
|
||||
|
||||
import JitsiMeetJS from './_';
|
||||
|
||||
|
||||
const JitsiConferenceErrors = JitsiMeetJS.errors.conference;
|
||||
const JitsiConnectionErrors = JitsiMeetJS.errors.connection;
|
||||
|
||||
/**
|
||||
* Creates a {@link JitsiLocalTrack} model from the given device id.
|
||||
*
|
||||
* @param {string} type - The media type of track being created. Expected values
|
||||
* are "video" or "audio".
|
||||
* @param {string} deviceId - The id of the target media source.
|
||||
* @param {number} [timeout] - A timeout for the JitsiMeetJS.createLocalTracks function call.
|
||||
* @param {Object} additionalOptions - Extra options to be passed to lib-jitsi-meet's {@code createLocalTracks}.
|
||||
*
|
||||
* @returns {Promise<JitsiLocalTrack>}
|
||||
*/
|
||||
export function createLocalTrack(type: string, deviceId: string | null, timeout?: number | null,
|
||||
additionalOptions?: Object) {
|
||||
return (
|
||||
JitsiMeetJS.createLocalTracks({
|
||||
cameraDeviceId: deviceId,
|
||||
devices: [ type ],
|
||||
micDeviceId: deviceId,
|
||||
timeout,
|
||||
...additionalOptions
|
||||
})
|
||||
.then(([ jitsiLocalTrack ]: any[]) => jitsiLocalTrack));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether analytics is enabled in a specific redux {@code store}.
|
||||
*
|
||||
* @param {IStateful} stateful - The redux store, state, or
|
||||
* {@code getState} function.
|
||||
* @returns {boolean} If analytics is enabled, {@code true}; {@code false},
|
||||
* otherwise.
|
||||
*/
|
||||
export function isAnalyticsEnabled(stateful: IStateful) {
|
||||
const { disableThirdPartyRequests, analytics = {} } = toState(stateful)['features/base/config'];
|
||||
|
||||
return !(disableThirdPartyRequests || analytics.disabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a specific {@link JitsiConferenceErrors} instance
|
||||
* indicates a fatal {@link JitsiConference} error.
|
||||
*
|
||||
* FIXME Figure out the category of errors defined by the function and describe
|
||||
* that category. I've currently named the category fatal because it appears to
|
||||
* be used in the cases of unrecoverable errors that necessitate a reload.
|
||||
*
|
||||
* @param {Error|string} error - The {@code JitsiConferenceErrors} instance to
|
||||
* categorize/classify or an {@link Error}-like object.
|
||||
* @returns {boolean} If the specified {@code JitsiConferenceErrors} instance
|
||||
* indicates a fatal {@code JitsiConference} error, {@code true}; otherwise,
|
||||
* {@code false}.
|
||||
*/
|
||||
export function isFatalJitsiConferenceError(error: Error | string) {
|
||||
if (typeof error !== 'string') {
|
||||
error = error.name; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
return (
|
||||
error === JitsiConferenceErrors.FOCUS_DISCONNECTED
|
||||
|| error === JitsiConferenceErrors.FOCUS_LEFT
|
||||
|| error === JitsiConferenceErrors.ICE_FAILED
|
||||
|| error === JitsiConferenceErrors.OFFER_ANSWER_FAILED
|
||||
|| error === JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a specific {@link JitsiConnectionErrors} instance
|
||||
* indicates a fatal {@link JitsiConnection} error.
|
||||
*
|
||||
* FIXME Figure out the category of errors defined by the function and describe
|
||||
* that category. I've currently named the category fatal because it appears to
|
||||
* be used in the cases of unrecoverable errors that necessitate a reload.
|
||||
*
|
||||
* @param {Error|string} error - The {@code JitsiConnectionErrors} instance to
|
||||
* categorize/classify or an {@link Error}-like object.
|
||||
* @returns {boolean} If the specified {@code JitsiConnectionErrors} instance
|
||||
* indicates a fatal {@code JitsiConnection} error, {@code true}; otherwise,
|
||||
* {@code false}.
|
||||
*/
|
||||
export function isFatalJitsiConnectionError(error: Error | string | ConnectionFailedError) {
|
||||
if (typeof error !== 'string') {
|
||||
error = error.name; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
return (
|
||||
error === JitsiConnectionErrors.CONNECTION_DROPPED_ERROR
|
||||
|| error === JitsiConnectionErrors.OTHER_ERROR
|
||||
|| error === JitsiConnectionErrors.SERVER_ERROR);
|
||||
}
|
||||
37
react/features/base/lib-jitsi-meet/functions.native.ts
Normal file
37
react/features/base/lib-jitsi-meet/functions.native.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
// @ts-ignore
|
||||
import { safeJsonParse } from '@jitsi/js-utils/json';
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import { loadScript } from '../util/loadScript.native';
|
||||
|
||||
import logger from './logger';
|
||||
|
||||
export * from './functions.any';
|
||||
|
||||
const { JavaScriptSandbox } = NativeModules;
|
||||
|
||||
/**
|
||||
* Loads config.js from a specific remote server.
|
||||
*
|
||||
* @param {string} url - The URL to load.
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export async function loadConfig(url: string): Promise<Object> {
|
||||
try {
|
||||
const configTxt = await loadScript(url, 10 * 1000 /* Timeout in ms */, true /* skipeval */);
|
||||
const configJson = await JavaScriptSandbox.evaluate(`${configTxt}\nJSON.stringify(config);`);
|
||||
const config = safeJsonParse(configJson);
|
||||
|
||||
if (typeof config !== 'object') {
|
||||
throw new Error('config is not an object');
|
||||
}
|
||||
|
||||
logger.info(`Config loaded from ${url}`);
|
||||
|
||||
return config;
|
||||
} catch (err) {
|
||||
logger.error(`Failed to load config from ${url}`, err);
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
14
react/features/base/lib-jitsi-meet/functions.web.ts
Normal file
14
react/features/base/lib-jitsi-meet/functions.web.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export * from './functions.any';
|
||||
|
||||
/**
|
||||
* Loads config.js from a specific remote server.
|
||||
*
|
||||
* @param {string} _url - The URL to load.
|
||||
* @returns {Promise<IConfig>}
|
||||
*/
|
||||
export async function loadConfig(_url?: string) {
|
||||
// Return "the config.js file" from the global scope - that is how the
|
||||
// Web app on both the client and the server was implemented before the
|
||||
// React Native app was even conceived.
|
||||
return window.config;
|
||||
}
|
||||
24
react/features/base/lib-jitsi-meet/index.ts
Normal file
24
react/features/base/lib-jitsi-meet/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
// Re-export JitsiMeetJS from the library lib-jitsi-meet to (the other features
|
||||
// of) the project jitsi-meet.
|
||||
import JitsiMeetJS from './_';
|
||||
export { JitsiMeetJS as default };
|
||||
|
||||
// XXX Re-export the properties exported by JitsiMeetJS in order to prevent
|
||||
// undefined imported JitsiMeetJS. It may be caused by import cycles but I have
|
||||
// not confirmed the theory.
|
||||
export const analytics = JitsiMeetJS.analytics;
|
||||
export const browser = JitsiMeetJS.util.browser;
|
||||
export const JitsiConferenceErrors = JitsiMeetJS.errors.conference;
|
||||
export const JitsiConferenceEvents = JitsiMeetJS.events.conference;
|
||||
export const JitsiConnectionErrors = JitsiMeetJS.errors.connection;
|
||||
export const JitsiConnectionEvents = JitsiMeetJS.events.connection;
|
||||
export const JitsiConnectionQualityEvents = JitsiMeetJS.events.connectionQuality;
|
||||
export const JitsiDetectionEvents = JitsiMeetJS.events.detection;
|
||||
export const JitsiE2ePingEvents = JitsiMeetJS.events.e2eping;
|
||||
export const JitsiMediaDevicesEvents = JitsiMeetJS.events.mediaDevices;
|
||||
export const JitsiTrackStreamingStatus = JitsiMeetJS.constants.trackStreamingStatus;
|
||||
export const JitsiRecordingConstants = JitsiMeetJS.constants.recording;
|
||||
export const JitsiSIPVideoGWStatus = JitsiMeetJS.constants.sipVideoGW;
|
||||
export const JitsiTrackErrors = JitsiMeetJS.errors.track;
|
||||
export const JitsiTrackEvents = JitsiMeetJS.events.track;
|
||||
export const RTCStatsEvents = JitsiMeetJS.events.rtcstats;
|
||||
3
react/features/base/lib-jitsi-meet/logger.ts
Normal file
3
react/features/base/lib-jitsi-meet/logger.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { getLogger } from '../logging/functions';
|
||||
|
||||
export default getLogger('features/base/lib-jitsi-meet');
|
||||
74
react/features/base/lib-jitsi-meet/middleware.ts
Normal file
74
react/features/base/lib-jitsi-meet/middleware.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
import { SET_CONFIG } from '../config/actionTypes';
|
||||
import { SET_NETWORK_INFO } from '../net-info/actionTypes';
|
||||
import { PARTICIPANT_LEFT } from '../participants/actionTypes';
|
||||
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
|
||||
|
||||
import JitsiMeetJS from './_';
|
||||
import { disposeLib, initLib } from './actions';
|
||||
|
||||
/**
|
||||
* Middleware that captures PARTICIPANT_LEFT action for a local participant
|
||||
* (which signalizes that we finally left the app) and disposes lib-jitsi-meet.
|
||||
* Also captures SET_CONFIG action and disposes previous instance (if any) of
|
||||
* lib-jitsi-meet, and initializes a new one with new config.
|
||||
*
|
||||
* @param {Store} store - Redux store.
|
||||
* @private
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case SET_NETWORK_INFO:
|
||||
JitsiMeetJS.setNetworkInfo({
|
||||
isOnline: action.isOnline
|
||||
});
|
||||
break;
|
||||
|
||||
case PARTICIPANT_LEFT:
|
||||
action.participant.local && store.dispatch(disposeLib());
|
||||
break;
|
||||
|
||||
case SET_CONFIG:
|
||||
return _setConfig(store, next, action);
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Notifies the feature base/lib-jitsi-meet that the action SET_CONFIG is being
|
||||
* dispatched within a specific Redux store.
|
||||
*
|
||||
* @param {Store} store - The Redux store in which the specified action is being
|
||||
* dispatched.
|
||||
* @param {Dispatch} next - The Redux dispatch function to dispatch the
|
||||
* specified action to the specified store.
|
||||
* @param {Action} action - The Redux action SET_CONFIG which is being
|
||||
* dispatched in the specified store.
|
||||
* @private
|
||||
* @returns {Object} The new state that is the result of the reduction of the
|
||||
* specified action.
|
||||
*/
|
||||
function _setConfig({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
|
||||
const { initialized } = getState()['features/base/lib-jitsi-meet'];
|
||||
|
||||
// XXX Since the config is changing, the library lib-jitsi-meet must be
|
||||
// initialized again with the new config. Consequently, it may need to be
|
||||
// disposed of first.
|
||||
// TODO Currently, disposeLib actually does not dispose of lib-jitsi-meet
|
||||
// because lib-jitsi-meet does not implement such functionality.
|
||||
if (initialized) {
|
||||
dispatch(disposeLib());
|
||||
}
|
||||
|
||||
// Let the new config into the Redux store (because initLib will read it
|
||||
// from there).
|
||||
const result = next(action);
|
||||
|
||||
dispatch(initLib());
|
||||
|
||||
return result;
|
||||
}
|
||||
45
react/features/base/lib-jitsi-meet/reducer.ts
Normal file
45
react/features/base/lib-jitsi-meet/reducer.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
|
||||
import {
|
||||
LIB_DID_DISPOSE,
|
||||
LIB_DID_INIT,
|
||||
LIB_INIT_ERROR
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* The default/initial redux state of the feature base/lib-jitsi-meet.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const DEFAULT_STATE = {};
|
||||
|
||||
export interface ILibJitsiMeetState {
|
||||
initError?: Error;
|
||||
initialized?: boolean;
|
||||
}
|
||||
|
||||
ReducerRegistry.register<ILibJitsiMeetState>(
|
||||
'features/base/lib-jitsi-meet',
|
||||
(state = DEFAULT_STATE, action): ILibJitsiMeetState => {
|
||||
switch (action.type) {
|
||||
case LIB_DID_DISPOSE:
|
||||
return DEFAULT_STATE;
|
||||
|
||||
case LIB_DID_INIT:
|
||||
return {
|
||||
...state,
|
||||
initError: undefined,
|
||||
initialized: true
|
||||
};
|
||||
|
||||
case LIB_INIT_ERROR:
|
||||
return {
|
||||
...state,
|
||||
initError: action.error,
|
||||
initialized: false
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user