theluyuan 38ba663466
Some checks failed
Close stale issues and PRs / stale (push) Has been cancelled
init
2025-09-02 14:49:16 +08:00

219 lines
7.3 KiB
TypeScript

import i18next from 'i18next';
import {
setPrejoinPageVisibility,
setSkipPrejoinOnReload
} from '../../prejoin/actions.web';
import { isPrejoinPageVisible } from '../../prejoin/functions';
import { iAmVisitor } from '../../visitors/functions';
import { CONNECTION_DISCONNECTED, CONNECTION_ESTABLISHED } from '../connection/actionTypes';
import { hangup } from '../connection/actions.web';
import { JitsiConferenceErrors, JitsiConnectionErrors, browser } from '../lib-jitsi-meet';
import { gumPending, setInitialGUMPromise } from '../media/actions';
import { MEDIA_TYPE } from '../media/constants';
import { IGUMPendingState } from '../media/types';
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
import { replaceLocalTrack } from '../tracks/actions.any';
import { getLocalTracks } from '../tracks/functions.any';
import {
CONFERENCE_FAILED,
CONFERENCE_JOINED,
CONFERENCE_JOIN_IN_PROGRESS,
CONFERENCE_LEFT,
KICKED_OUT
} from './actionTypes';
import { TRIGGER_READY_TO_CLOSE_REASONS } from './constants';
import logger from './logger';
import './middleware.any';
let screenLock: WakeLockSentinel | undefined;
/**
* Releases the screen lock.
*
* @returns {Promise}
*/
async function releaseScreenLock() {
if (screenLock) {
if (!screenLock.released) {
logger.debug('Releasing wake lock.');
try {
await screenLock.release();
} catch (e) {
logger.error(`Error while releasing the screen wake lock: ${e}.`);
}
}
screenLock.removeEventListener('release', onWakeLockReleased);
screenLock = undefined;
document.removeEventListener('visibilitychange', handleVisibilityChange);
}
}
/**
* Requests a new screen wake lock.
*
* @returns {void}
*/
function requestWakeLock() {
if (navigator.wakeLock?.request) {
navigator.wakeLock.request('screen')
.then(lock => {
screenLock = lock;
screenLock.addEventListener('release', onWakeLockReleased);
document.addEventListener('visibilitychange', handleVisibilityChange);
logger.debug('Wake lock created.');
})
.catch(e => {
logger.error(`Error while requesting wake lock for screen: ${e}`);
});
}
}
/**
* Page visibility change handler that re-requests the wake lock if it has been released by the OS.
*
* @returns {void}
*/
async function handleVisibilityChange() {
if (screenLock?.released && document.visibilityState === 'visible') {
// The screen lock have been released by the OS because of document visibility change. Lets try to request the
// wake lock again.
await releaseScreenLock();
requestWakeLock();
}
}
/**
* Wake lock released handler.
*
* @returns {void}
*/
function onWakeLockReleased() {
logger.debug('Wake lock released');
}
MiddlewareRegistry.register(store => next => action => {
const { dispatch, getState } = store;
const { enableForcedReload } = getState()['features/base/config'];
switch (action.type) {
case CONFERENCE_JOIN_IN_PROGRESS: {
dispatch(setPrejoinPageVisibility(false));
break;
}
case CONFERENCE_JOINED: {
if (enableForcedReload) {
dispatch(setSkipPrejoinOnReload(false));
}
requestWakeLock();
break;
}
case CONFERENCE_FAILED: {
const errorName = action.error?.name;
if (enableForcedReload
&& (errorName === JitsiConferenceErrors.CONFERENCE_RESTARTED
|| errorName === JitsiConnectionErrors.SHARD_CHANGED_ERROR)) {
dispatch(setSkipPrejoinOnReload(true));
}
if (errorName === JitsiConferenceErrors.CONFERENCE_DESTROYED) {
const state = getState();
const { notifyOnConferenceDestruction = true } = state['features/base/config'];
const [ reason ] = action.error.params;
const titlekey = Object.keys(TRIGGER_READY_TO_CLOSE_REASONS)[
Object.values(TRIGGER_READY_TO_CLOSE_REASONS).indexOf(reason)
];
dispatch(hangup(true, i18next.t(titlekey) || reason, notifyOnConferenceDestruction));
}
releaseScreenLock();
break;
}
case CONFERENCE_LEFT:
case KICKED_OUT:
releaseScreenLock();
break;
case CONNECTION_DISCONNECTED: {
const { initialGUMPromise } = getState()['features/base/media'];
if (initialGUMPromise) {
store.dispatch(setInitialGUMPromise());
}
break;
}
case CONNECTION_ESTABLISHED: {
const { initialGUMPromise } = getState()['features/base/media'];
const promise = initialGUMPromise ? initialGUMPromise.promise : Promise.resolve({ tracks: [] });
const prejoinVisible = isPrejoinPageVisible(getState());
logger.debug(`On connection established: prejoinVisible: ${prejoinVisible}, initialGUMPromiseExists=${
Boolean(initialGUMPromise)}, promiseExists=${Boolean(promise)}`);
if (prejoinVisible) {
promise.then(() => {
const state = getState();
let localTracks = getLocalTracks(state['features/base/tracks']);
const trackReplacePromises = [];
// Do not signal audio/video tracks if the user joins muted.
for (const track of localTracks) {
// Always add the audio track on Safari because of a known issue where audio playout doesn't happen
// if the user joins audio and video muted.
if ((track.muted && !(browser.isWebKitBased() && track.jitsiTrack
&& track.jitsiTrack.getType() === MEDIA_TYPE.AUDIO)) || iAmVisitor(state)) {
trackReplacePromises.push(dispatch(replaceLocalTrack(track.jitsiTrack, null))
.catch((error: any) => {
logger.error(`Failed to replace local track (${track.jitsiTrack}) with null: ${error}`);
}));
}
}
Promise.allSettled(trackReplacePromises).then(() => {
// Re-fetch the local tracks after muted tracks have been removed above.
// This is needed, because the tracks are effectively disposed by the replaceLocalTrack and should
// not be used anymore.
localTracks = getLocalTracks(getState()['features/base/tracks']);
const jitsiTracks = localTracks.map((t: any) => t.jitsiTrack);
return APP.conference.startConference(jitsiTracks);
})
.catch(logger.error);
});
} else {
promise.then(({ tracks }) => {
let tracksToUse = tracks ?? [];
if (iAmVisitor(getState())) {
tracksToUse = [];
tracks.forEach(track => track.dispose().catch(logger.error));
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
}
dispatch(setInitialGUMPromise());
return APP.conference.startConference(tracksToUse);
})
.catch(logger.error);
}
break;
}
}
return next(action);
});