fix: Make all middleware functions sync.

Some middleware functions are declared as async. This wraps next(action) in Promise which will delay the execution of actions and also dispatch will return the its result always as a Promise.
pull/14947/head jitsi-meet_9638
Hristo Terezov 11 months ago
parent b242900619
commit 2514617417
  1. 3
      conference.js
  2. 2
      react/features/base/app/middleware.web.ts
  3. 2
      react/features/base/audio-only/actions.ts
  4. 24
      react/features/base/conference/actions.any.ts
  5. 7
      react/features/base/conference/middleware.any.ts
  6. 7
      react/features/base/connection/actions.web.ts
  7. 9
      react/features/base/i18n/middleware.ts
  8. 4
      react/features/base/media/actions.ts
  9. 20
      react/features/base/tracks/actions.any.ts
  10. 2
      react/features/base/tracks/middleware.any.ts
  11. 2
      react/features/jaas/middleware.any.ts
  12. 2
      react/features/jaas/middleware.web.ts
  13. 16
      react/features/lobby/actions.any.ts
  14. 4
      react/features/no-audio-signal/middleware.tsx
  15. 4
      react/features/noise-detection/middleware.ts
  16. 4
      react/features/prejoin/actions.web.ts
  17. 2
      react/features/prejoin/middleware.web.ts
  18. 4
      react/features/recording/actions.any.ts
  19. 4
      react/features/recording/components/LiveStream/AbstractLiveStreamButton.ts
  20. 4
      react/features/recording/components/Recording/AbstractHighlightButton.ts
  21. 4
      react/features/recording/components/Recording/AbstractRecordButton.ts
  22. 2
      react/features/recording/components/Recording/LocalRecordingManager.native.ts
  23. 2
      react/features/recording/components/Recording/LocalRecordingManager.web.ts
  24. 4
      react/features/recording/components/Recording/web/HighlightButton.tsx
  25. 130
      react/features/recording/middleware.ts
  26. 2
      react/features/remote-control/middleware.ts
  27. 4
      react/features/subtitles/components/AbstractClosedCaptionButton.tsx
  28. 4
      react/features/talk-while-muted/middleware.ts
  29. 4
      react/features/virtual-background/actions.ts
  30. 7
      react/features/visitors/actions.ts
  31. 3
      react/features/visitors/middleware.ts
  32. 67
      react/features/web-hid/middleware.ts
  33. 2
      react/features/whiteboard/actions.web.ts
  34. 2
      react/features/whiteboard/middleware.native.ts
  35. 44
      react/features/whiteboard/middleware.web.ts

@ -1165,11 +1165,12 @@ export default {
APP.store.dispatch(gumPending(mutedTrackTypes, IGUMPendingState.NONE));
}
this._setLocalAudioVideoStreams(tracks);
this._room = room; // FIXME do not use this
APP.store.dispatch(_conferenceWillJoin(room));
this._setLocalAudioVideoStreams(tracks);
sendLocalParticipant(APP.store, room);
this._setupListeners();

@ -18,7 +18,7 @@ let pressureObserver: typeof window.PressureObserver;
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(() => (next: Function) => async (action: AnyAction) => {
MiddlewareRegistry.register(() => (next: Function) => (action: AnyAction) => {
switch (action.type) {
case APP_WILL_MOUNT: {

@ -46,6 +46,6 @@ export function toggleAudioOnly() {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const { enabled } = getState()['features/base/audio-only'];
return dispatch(setAudioOnly(!enabled));
dispatch(setAudioOnly(!enabled));
};
}

@ -875,7 +875,7 @@ export function setPassword(
password?: string) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
if (!conference) {
return;
return Promise.reject();
}
switch (method) {
case conference.join: {
@ -981,7 +981,7 @@ export function setStartMutedPolicy(
video: startVideoMuted
});
return dispatch(
dispatch(
onStartMutedPolicyChanged(startAudioMuted, startVideoMuted));
};
}
@ -1058,14 +1058,20 @@ export function redirect(vnode: string, focusJid: string, username: string) {
return;
}
dispatch(overwriteConfig(newConfig)) // @ts-ignore
.then(() => dispatch(disconnect(true)))
.then(() => dispatch(setIAmVisitor(Boolean(vnode))))
dispatch(overwriteConfig(newConfig));
dispatch(disconnect(true))
.then(() => {
dispatch(setIAmVisitor(Boolean(vnode)));
// we do not clear local tracks on error, so we need to manually clear them
return dispatch(destroyLocalTracks());
})
.then(() => {
dispatch(conferenceWillInit());
// we do not clear local tracks on error, so we need to manually clear them
.then(() => dispatch(destroyLocalTracks()))
.then(() => dispatch(conferenceWillInit()))
.then(() => dispatch(connect()))
return dispatch(connect());
})
.then(() => {
const media: Array<MediaType> = [];

@ -205,9 +205,10 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
const newConfig = restoreConferenceOptions(getState);
if (newConfig) {
dispatch(overwriteConfig(newConfig)) // @ts-ignore
.then(() => dispatch(conferenceWillLeave(conference)))
.then(() => conference.leave())
dispatch(overwriteConfig(newConfig));
dispatch(conferenceWillLeave(conference));
conference.leave()
.then(() => dispatch(disconnect()))
.then(() => dispatch(connect()))
.then(() => {

@ -34,8 +34,11 @@ export function connect(id?: string, password?: string) {
return getJaasJWT(state);
}
})
.then(j => j && dispatch(setJWT(j)))
.then(() => dispatch(_connectInternal(id, password)));
.then(j => {
j && dispatch(setJWT(j));
return dispatch(_connectInternal(id, password));
});
}
// used by jibri

@ -12,7 +12,7 @@ import logger from './logger';
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => async action => {
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case I18NEXT_INITIALIZED:
case LANGUAGE_CHANGED:
@ -23,11 +23,10 @@ MiddlewareRegistry.register(store => next => async action => {
: store.getState()['features/dynamic-branding'];
if (language && labels && labels[language]) {
try {
await changeLanguageBundle(language, labels[language]);
} catch (err) {
changeLanguageBundle(language, labels[language])
.catch(err => {
logger.log('Error setting dynamic language bundle', err);
}
});
}
break;
}

@ -122,7 +122,7 @@ export function setScreenshareMuted(
// eslint-disable-next-line no-bitwise
const newValue = muted ? oldValue | authority : oldValue & ~authority;
return dispatch({
dispatch({
type: SET_SCREENSHARE_MUTED,
authority,
ensureTrack,
@ -180,7 +180,7 @@ export function setVideoMuted(
// eslint-disable-next-line no-bitwise
const newValue = muted ? oldValue | authority : oldValue & ~authority;
return dispatch({
dispatch({
type: SET_VIDEO_MUTED,
authority,
ensureTrack,

@ -64,7 +64,7 @@ export function addLocalTrack(newTrack: any) {
const isMuted = newTrack.isMuted();
logger.log(`Adding ${newTrack.getType()} track - ${isMuted ? 'muted' : 'unmuted'}`);
await dispatch(setMuted(isMuted));
dispatch(setMuted(isMuted));
return dispatch(_addTracks([ newTrack ]));
};
@ -233,12 +233,11 @@ export function createLocalTracksA(options: ITrackOptions = {}) {
*/
export function destroyLocalTracks(track: any = null) {
if (track) {
return (dispatch: IStore['dispatch']) => {
dispatch(_disposeAndRemoveTracks([ track ]));
};
return (dispatch: IStore['dispatch']) => dispatch(_disposeAndRemoveTracks([ track ]));
}
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) =>
// First wait until any getUserMedia in progress is settled and then get
// rid of all local tracks.
_cancelGUMProcesses(getState)
@ -248,7 +247,6 @@ export function destroyLocalTracks(track: any = null) {
getState()['features/base/tracks']
.filter(t => t.local)
.map(t => t.jitsiTrack))));
};
}
/**
@ -274,7 +272,7 @@ export function noDataFromSource(track: any) {
* @returns {Function}
*/
export function showNoDataFromSourceVideoError(jitsiTrack: any) {
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
let notificationInfo;
const track = getTrackByJitsiTrack(getState()['features/base/tracks'], jitsiTrack);
@ -286,7 +284,7 @@ export function showNoDataFromSourceVideoError(jitsiTrack: any) {
if (track.isReceivingData) {
notificationInfo = undefined;
} else {
const notificationAction = await dispatch(showErrorNotification({
const notificationAction = dispatch(showErrorNotification({
descriptionKey: 'dialog.cameraNotSendingData',
titleKey: 'dialog.cameraNotSendingDataTitle'
}, NOTIFICATION_TIMEOUT_TYPE.LONG));
@ -359,7 +357,7 @@ function replaceStoredTracks(oldTrack: any, newTrack: any) {
sendAnalytics(createTrackMutedEvent(newTrack.getType(), 'track.replaced', isMuted));
logger.log(`Replace ${newTrack.getType()} track - ${isMuted ? 'muted' : 'unmuted'}`);
await dispatch(setMuted(isMuted));
dispatch(setMuted(isMuted));
await dispatch(_addTracks([ newTrack ]));
}
};
@ -373,7 +371,7 @@ function replaceStoredTracks(oldTrack: any, newTrack: any) {
* @returns {Function}
*/
export function trackAdded(track: any) {
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
track.on(
JitsiTrackEvents.TRACK_MUTE_CHANGED,
() => dispatch(trackMutedChanged(track)));
@ -400,7 +398,7 @@ export function trackAdded(track: any) {
track.on(JitsiTrackEvents.NO_DATA_FROM_SOURCE, () => dispatch(noDataFromSource({ jitsiTrack: track })));
if (!isReceivingData) {
if (mediaType === MEDIA_TYPE.AUDIO) {
const notificationAction = await dispatch(showNotification({
const notificationAction = dispatch(showNotification({
descriptionKey: 'dialog.micNotSendingData',
titleKey: 'dialog.micNotSendingDataTitle'
}, NOTIFICATION_TIMEOUT_TYPE.LONG));

@ -183,7 +183,7 @@ function _getLocalTrack(
* @private
* @returns {void}
*/
async function _setMuted(store: IStore, { ensureTrack, muted }: {
function _setMuted(store: IStore, { ensureTrack, muted }: {
ensureTrack: boolean; muted: boolean; }, mediaType: MediaType) {
const { dispatch, getState } = store;
const localTrack = _getLocalTrack(store, mediaType, /* includePending */ true);

@ -13,7 +13,7 @@ import { getVpaasTenant, isVpaasMeeting } from './functions';
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => async action => {
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case CONFERENCE_JOINED: {
_maybeTrackVpaasConferenceJoin(store.getState());

@ -16,7 +16,7 @@ import logger from './logger';
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => async action => {
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case CONFERENCE_JOINED: {
const { conference } = action;

@ -32,7 +32,7 @@ import { IKnockingParticipant } from './types';
* @returns {Function}
*/
export function joinWithPassword(password: string) {
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const conference = getCurrentConference(getState);
dispatch(setPassword(conference, conference?.join, password));
@ -79,7 +79,7 @@ export function participantIsKnockingOrUpdated(participant: IKnockingParticipant
* @returns {Function}
*/
export function answerKnockingParticipant(id: string, approved: boolean) {
return async (dispatch: IStore['dispatch']) => {
return (dispatch: IStore['dispatch']) => {
dispatch(setKnockingParticipantApproval(id, approved));
dispatch(hideNotification(LOBBY_NOTIFICATION_ID));
};
@ -202,7 +202,7 @@ export function setPasswordJoinFailed(failed: boolean) {
* @returns {Function}
*/
export function startKnocking() {
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();
const { membersOnly } = state['features/base/conference'];
@ -287,7 +287,7 @@ export function hideLobbyScreen() {
* @returns {Promise<void>}
*/
export function handleLobbyChatInitialized(payload: { attendee: IParticipant; moderator: IParticipant; }) {
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();
const conference = getCurrentConference(state);
@ -323,7 +323,7 @@ export function handleLobbyChatInitialized(payload: { attendee: IParticipant; mo
* @returns {Promise<void>}
*/
export function onSendMessage(message: string) {
return async (dispatch: IStore['dispatch']) => {
return (dispatch: IStore['dispatch']) => {
dispatch(sendMessage(message));
};
}
@ -349,7 +349,7 @@ export function sendLobbyChatMessage(message: Object) {
* @returns {Function}
*/
export function maybeSetLobbyChatMessageListener() {
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();
const lobbyEnabled = getLobbyEnabled(state);
@ -366,7 +366,7 @@ export function maybeSetLobbyChatMessageListener() {
* @returns {Function}
*/
export function updateLobbyParticipantOnLeave(participantId: string) {
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();
const { knocking, knockingParticipants } = state['features/lobby'];
const { lobbyMessageRecipient } = state['features/chat'];
@ -400,7 +400,7 @@ export function updateLobbyParticipantOnLeave(participantId: string) {
* @returns {Function}
*/
export function setLobbyMessageListener() {
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();
const conference = getCurrentConference(state);
const { enableChat = true } = getLobbyConfig(state);

@ -18,7 +18,7 @@ import DialInLink from './components/DialInLink';
import { NO_AUDIO_SIGNAL_SOUND_ID } from './constants';
import { NO_AUDIO_SIGNAL_SOUND_FILE } from './sounds';
MiddlewareRegistry.register(store => next => async action => {
MiddlewareRegistry.register(store => next => action => {
const result = next(action);
const { dispatch } = store;
@ -107,7 +107,7 @@ async function _handleNoAudioSignalNotification({ dispatch, getState }: IStore,
} ];
}
const notification = await dispatch(showNotification({
const notification = dispatch(showNotification({
titleKey: 'toolbar.noAudioSignalTitle',
description: <DialInLink />,
descriptionKey,

@ -36,8 +36,8 @@ MiddlewareRegistry.register(store => next => action => {
}
});
conference.on(
JitsiConferenceEvents.NOISY_MIC, async () => {
const notification = await dispatch(showNotification({
JitsiConferenceEvents.NOISY_MIC, () => {
const notification = dispatch(showNotification({
titleKey: 'toolbar.noisyAudioInputTitle',
descriptionKey: 'toolbar.noisyAudioInputDesc'
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));

@ -197,7 +197,7 @@ export function dialOut(onSuccess: Function, onFail: Function) {
* @returns {Function}
*/
export function initPrejoin(tracks: Object[], errors: Object) {
return async function(dispatch: IStore['dispatch']) {
return function(dispatch: IStore['dispatch']) {
dispatch(setPrejoinDeviceErrors(errors));
dispatch(prejoinInitialized());
@ -216,7 +216,7 @@ export function initPrejoin(tracks: Object[], errors: Object) {
*/
export function joinConference(options?: Object, ignoreJoiningInProgress = false,
jid?: string, password?: string) {
return async function(dispatch: IStore['dispatch'], getState: IStore['getState']) {
return function(dispatch: IStore['dispatch'], getState: IStore['getState']) {
if (!ignoreJoiningInProgress) {
const state = getState();
const { joiningInProgress } = state['features/prejoin'];

@ -24,7 +24,7 @@ import { isPrejoinPageVisible } from './functions';
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => async action => {
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case SET_AUDIO_MUTED: {
if (isPrejoinPageVisible(store.getState())) {

@ -138,7 +138,7 @@ export function setLiveStreamKey(streamKey: string) {
* @returns {Function}
*/
export function showPendingRecordingNotification(streamType: string) {
return async (dispatch: IStore['dispatch']) => {
return (dispatch: IStore['dispatch']) => {
const isLiveStreaming
= streamType === JitsiMeetJS.constants.recording.mode.STREAM;
const dialogProps = isLiveStreaming ? {
@ -148,7 +148,7 @@ export function showPendingRecordingNotification(streamType: string) {
descriptionKey: 'recording.pending',
titleKey: 'dialog.recording'
};
const notification = await dispatch(showNotification({
const notification = dispatch(showNotification({
...dialogProps
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));

@ -72,10 +72,10 @@ export default class AbstractLiveStreamButton<P extends IProps> extends Abstract
* @protected
* @returns {void}
*/
async _handleClick() {
_handleClick() {
const { dispatch } = this.props;
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
const dialogShown = dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
if (!dialogShown) {
this._onHandleClick();

@ -77,9 +77,9 @@ export default class AbstractHighlightButton<P extends IProps, S={}> extends Com
titleKey: 'recording.highlightMoment',
uid: PROMPT_RECORDING_NOTIFICATION_ID,
customActionNameKey: [ 'localRecording.start' ],
customActionHandler: [ async () => {
customActionHandler: [ () => {
dispatch(hideNotification(PROMPT_RECORDING_NOTIFICATION_ID));
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
const dialogShown = dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
if (!dialogShown) {
dispatch(openDialog(StartRecordingDialog));

@ -69,7 +69,7 @@ export default class AbstractRecordButton<P extends IProps> extends AbstractButt
* @protected
* @returns {void}
*/
async _handleClick() {
_handleClick() {
const { _isRecordingRunning, dispatch } = this.props;
sendAnalytics(createToolbarEvent(
@ -78,7 +78,7 @@ export default class AbstractRecordButton<P extends IProps> extends AbstractButt
'is_recording': _isRecordingRunning,
type: JitsiRecordingConstants.mode.FILE
}));
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
const dialogShown = dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
if (!dialogShown) {
this._onHandleClick();

@ -7,7 +7,7 @@ interface ILocalRecordingManager {
on: boolean;
withVideo: boolean;
};
startLocalRecording: (store: IStore, onlySelf: boolean) => void;
startLocalRecording: (store: IStore, onlySelf: boolean) => Promise<void>;
stopLocalRecording: () => void;
}

@ -28,7 +28,7 @@ interface ILocalRecordingManager {
roomName: string;
saveRecording: (recordingData: Blob[], filename: string) => void;
selfRecording: ISelfRecording;
startLocalRecording: (store: IStore, onlySelf: boolean) => void;
startLocalRecording: (store: IStore, onlySelf: boolean) => Promise<void>;
stopLocalRecording: () => void;
stream: MediaStream | undefined;
totalSize: number;

@ -133,9 +133,9 @@ export class HighlightButton extends AbstractHighlightButton<IProps, IState> {
*
* @returns {void}
*/
async _onOpenDialog() {
_onOpenDialog() {
const { dispatch } = this.props;
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
const dialogShown = dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
if (!dialogShown) {
dispatch(openDialog(StartRecordingDialog));

@ -72,7 +72,7 @@ StateListenerRegistry.register(
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(({ dispatch, getState }) => next => async action => {
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
let oldSessionData;
if (action.type === RECORDING_SESSION_UPDATED) {
@ -114,9 +114,11 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => async action =>
const { localRecording } = getState()['features/base/config'];
const { onlySelf } = action;
try {
await LocalRecordingManager.startLocalRecording({ dispatch,
getState }, action.onlySelf);
LocalRecordingManager.startLocalRecording({
dispatch,
getState
}, action.onlySelf)
.then(() => {
const props = {
descriptionKey: 'recording.on',
titleKey: 'dialog.recording'
@ -136,7 +138,8 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => async action =>
APP.API.notifyRecordingStatusChanged(
true, 'local', undefined, isRecorderTranscriptionsRunning(getState()));
}
} catch (err: any) {
})
.catch(err => {
logger.error('Capture failed', err);
let descriptionKey = 'recording.error';
@ -160,7 +163,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => async action =>
}
dispatch(showErrorNotification(props, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
}
});
break;
}
@ -205,78 +208,79 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => async action =>
if (updatedSessionData?.status === PENDING && oldSessionData?.status !== PENDING) {
dispatch(showPendingRecordingNotification(mode));
dispatch(hideNotification(START_RECORDING_NOTIFICATION_ID));
} else {
dispatch(hidePendingRecordingNotification(mode));
if (updatedSessionData?.status === ON) {
// We receive 2 updates of the session status ON. The first one is from jibri when it joins.
// The second one is from jicofo which will deliever the initiator value. Since the start
// recording notification uses the initiator value we skip the jibri update and show the
// notification on the update from jicofo.
// FIXE: simplify checks when the backend start sending only one status ON update containing the
// initiator.
if (initiator && !oldSessionData?.initiator) {
if (typeof recordingLimit === 'object') {
dispatch(showRecordingLimitNotification(mode));
} else {
dispatch(showStartedRecordingNotification(mode, initiator, action.sessionData.id));
}
}
if (oldSessionData?.status !== ON) {
sendAnalytics(createRecordingEvent('start', mode));
let soundID;
if (mode === JitsiRecordingConstants.mode.FILE && !isRecorderTranscriptionsRunning(state)) {
soundID = RECORDING_ON_SOUND_ID;
} else if (mode === JitsiRecordingConstants.mode.STREAM) {
soundID = LIVE_STREAMING_ON_SOUND_ID;
}
if (soundID) {
dispatch(playSound(soundID));
}
break;
}
if (typeof APP !== 'undefined') {
APP.API.notifyRecordingStatusChanged(
true, mode, undefined, isRecorderTranscriptionsRunning(state));
}
}
} else if (updatedSessionData?.status === OFF && oldSessionData?.status !== OFF) {
if (terminator) {
dispatch(
showStoppedRecordingNotification(
mode, getParticipantDisplayName(state, getResourceId(terminator))));
dispatch(hidePendingRecordingNotification(mode));
if (updatedSessionData?.status === ON) {
// We receive 2 updates of the session status ON. The first one is from jibri when it joins.
// The second one is from jicofo which will deliever the initiator value. Since the start
// recording notification uses the initiator value we skip the jibri update and show the
// notification on the update from jicofo.
// FIXE: simplify checks when the backend start sending only one status ON update containing the
// initiator.
if (initiator && !oldSessionData?.initiator) {
if (typeof recordingLimit === 'object') {
dispatch(showRecordingLimitNotification(mode));
} else {
dispatch(showStartedRecordingNotification(mode, initiator, action.sessionData.id));
}
}
let duration = 0, soundOff, soundOn;
if (oldSessionData?.status !== ON) {
sendAnalytics(createRecordingEvent('start', mode));
if (oldSessionData?.timestamp) {
duration
= (Date.now() / 1000) - oldSessionData.timestamp;
}
sendAnalytics(createRecordingEvent('stop', mode, duration));
let soundID;
if (mode === JitsiRecordingConstants.mode.FILE && !isRecorderTranscriptionsRunning(state)) {
soundOff = RECORDING_OFF_SOUND_ID;
soundOn = RECORDING_ON_SOUND_ID;
soundID = RECORDING_ON_SOUND_ID;
} else if (mode === JitsiRecordingConstants.mode.STREAM) {
soundOff = LIVE_STREAMING_OFF_SOUND_ID;
soundOn = LIVE_STREAMING_ON_SOUND_ID;
soundID = LIVE_STREAMING_ON_SOUND_ID;
}
if (soundOff && soundOn) {
dispatch(stopSound(soundOn));
dispatch(playSound(soundOff));
if (soundID) {
dispatch(playSound(soundID));
}
if (typeof APP !== 'undefined') {
APP.API.notifyRecordingStatusChanged(
false, mode, undefined, isRecorderTranscriptionsRunning(state));
true, mode, undefined, isRecorderTranscriptionsRunning(state));
}
}
} else if (updatedSessionData?.status === OFF && oldSessionData?.status !== OFF) {
if (terminator) {
dispatch(
showStoppedRecordingNotification(
mode, getParticipantDisplayName(state, getResourceId(terminator))));
}
let duration = 0, soundOff, soundOn;
if (oldSessionData?.timestamp) {
duration
= (Date.now() / 1000) - oldSessionData.timestamp;
}
sendAnalytics(createRecordingEvent('stop', mode, duration));
if (mode === JitsiRecordingConstants.mode.FILE && !isRecorderTranscriptionsRunning(state)) {
soundOff = RECORDING_OFF_SOUND_ID;
soundOn = RECORDING_ON_SOUND_ID;
} else if (mode === JitsiRecordingConstants.mode.STREAM) {
soundOff = LIVE_STREAMING_OFF_SOUND_ID;
soundOn = LIVE_STREAMING_ON_SOUND_ID;
}
if (soundOff && soundOn) {
dispatch(stopSound(soundOn));
dispatch(playSound(soundOff));
}
if (typeof APP !== 'undefined') {
APP.API.notifyRecordingStatusChanged(
false, mode, undefined, isRecorderTranscriptionsRunning(state));
}
}
break;

@ -19,7 +19,7 @@ import './subscriber';
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => async action => {
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case APP_WILL_MOUNT: {
const { dispatch } = store;

@ -49,7 +49,7 @@ export class AbstractClosedCaptionButton
* @protected
* @returns {void}
*/
async _handleClick() {
_handleClick() {
const { _requestingSubtitles, dispatch } = this.props;
sendAnalytics(createToolbarEvent('transcribing.ccButton',
@ -57,7 +57,7 @@ export class AbstractClosedCaptionButton
'requesting_subtitles': Boolean(_requestingSubtitles)
}));
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
const dialogShown = dispatch(maybeShowPremiumFeatureDialog(MEET_FEATURES.RECORDING));
if (!dialogShown) {
this._handleClickOpenLanguageSelector();

@ -41,14 +41,14 @@ MiddlewareRegistry.register(store => next => action => {
}
});
conference.on(
JitsiConferenceEvents.TALK_WHILE_MUTED, async () => {
JitsiConferenceEvents.TALK_WHILE_MUTED, () => {
const state = getState();
const local = getLocalParticipant(state);
// Display the talk while muted notification only when the audio button is not disabled.
if (!isAudioMuteButtonDisabled(state)) {
const forceMuted = isForceMuted(local, MEDIA_TYPE.AUDIO, state);
const notification = await dispatch(showNotification({
const notification = dispatch(showNotification({
titleKey: 'toolbar.talkWhileMutedPopup',
customActionNameKey: [ forceMuted ? 'notify.raiseHandAction' : 'notify.unmute' ],
customActionHandler: [ () => dispatch(forceMuted ? raiseHand(true) : setAudioMuted(false)) ]

@ -14,8 +14,8 @@ import { IVirtualBackground } from './reducer';
*/
export function toggleBackgroundEffect(options: IVirtualBackground, jitsiTrack: any) {
return async function(dispatch: IStore['dispatch'], getState: IStore['getState']) {
await dispatch(backgroundEnabled(options.backgroundEffectEnabled));
await dispatch(setVirtualBackground(options));
dispatch(backgroundEnabled(options.backgroundEffectEnabled));
dispatch(setVirtualBackground(options));
const state = getState();
const virtualBackground = state['features/virtual-background'];

@ -93,8 +93,11 @@ export function demoteRequest(id: string) {
if (id === localParticipant?.id) {
dispatch(disconnect(true))
.then(() => dispatch(setPreferVisitor(true)))
.then(() => dispatch(connect()));
.then(() => {
dispatch(setPreferVisitor(true));
return dispatch(connect());
});
} else {
conference?.sendMessage({
type: 'visitors',

@ -110,8 +110,9 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
if (localParticipant && localParticipant.id === msg.id) {
// handle demote
dispatch(disconnect(true))
.then(() => dispatch(setPreferVisitor(true)))
.then(() => {
dispatch(setPreferVisitor(true));
// we need to set the name, so we can use it later in the notification
if (participantById) {
dispatch(setVisitorDemoteActor(participantById.name));

@ -33,7 +33,7 @@ let updateDeviceListener: (e: any) => void;
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register((store: IStore) => next => async action => {
MiddlewareRegistry.register((store: IStore) => next => action => {
const { dispatch, getState } = store;
if (!getWebHIDFeatureConfig(getState())) {
@ -88,33 +88,7 @@ MiddlewareRegistry.register((store: IStore) => next => async action => {
break;
}
case REQUEST_HID_DEVICE: {
const hidManager = getWebHidInstance();
const availableDevices = await hidManager.requestHidDevices();
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
if (!availableDevices || !availableDevices.length) {
logger.info('HID device not available');
break;
}
const _initDeviceListener = (e: CustomEvent<{ deviceInfo: IDeviceInfo; }>) =>
dispatch(initDeviceInfo(e.detail.deviceInfo));
const _updateDeviceListener
= (e: CustomEvent<{ actionResult: { eventName: string; }; deviceInfo: IDeviceInfo; }>) => {
handleUpdateHidDevice(dispatch, e);
};
initDeviceListener = _initDeviceListener;
updateDeviceListener = _updateDeviceListener;
attachHidEventListeners(initDeviceListener, updateDeviceListener);
await hidManager.listenToConnectedHid();
// sync headset to mute if participant is already muted.
if (isAudioMuted(store.getState())) {
hidManager.sendDeviceReport({ command: COMMANDS.MUTE_ON });
}
_onRequestHIDDevice(store);
break;
}
@ -132,3 +106,40 @@ MiddlewareRegistry.register((store: IStore) => next => async action => {
return next(action);
});
/**
* Handles HID device requests.
*
* @param {IStore} store - The redux store.
* @returns {Promise}
*/
async function _onRequestHIDDevice(store: IStore) {
const { dispatch } = store;
const hidManager = getWebHidInstance();
const availableDevices = await hidManager.requestHidDevices();
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
if (!availableDevices || !availableDevices.length) {
logger.info('HID device not available');
return;
}
const _initDeviceListener = (e: CustomEvent<{ deviceInfo: IDeviceInfo; }>) =>
dispatch(initDeviceInfo(e.detail.deviceInfo));
const _updateDeviceListener
= (e: CustomEvent<{ actionResult: { eventName: string; }; deviceInfo: IDeviceInfo; }>) => {
handleUpdateHidDevice(dispatch, e);
};
initDeviceListener = _initDeviceListener;
updateDeviceListener = _updateDeviceListener;
attachHidEventListeners(initDeviceListener, updateDeviceListener);
await hidManager.listenToConnectedHid();
// sync headset to mute if participant is already muted.
if (isAudioMuted(store.getState())) {
hidManager.sendDeviceReport({ command: COMMANDS.MUTE_ON });
}
}

@ -14,7 +14,7 @@ export * from './actions.any';
* @returns {Function}
*/
export function toggleWhiteboard() {
return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();
const isAllowed = isWhiteboardAllowed(state);
const isOpen = isWhiteboardOpen(state);

@ -30,7 +30,7 @@ import './middleware.any';
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register((store: IStore) => (next: Function) => async (action: AnyAction) => {
MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyAction) => {
const { dispatch, getState } = store;
const state = getState();

@ -59,7 +59,7 @@ const focusWhiteboard = (store: IStore) => {
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register((store: IStore) => (next: Function) => async (action: AnyAction) => {
MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyAction) => {
const { dispatch, getState } = store;
const state = getState();
const conference = getCurrentConference(state);
@ -78,21 +78,7 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => async (action
}
if (!existingCollabDetails) {
const collabLinkData = await generateCollaborationLinkData();
const collabServerUrl = generateCollabServerUrl(state);
const roomId = getCurrentRoomId(state);
const collabData = {
collabDetails: {
roomId,
roomKey: collabLinkData.roomKey
},
collabServerUrl
};
focusWhiteboard(store);
dispatch(setupWhiteboard(collabData));
conference?.getMetadataHandler().setMetadata(WHITEBOARD_ID, collabData);
raiseWhiteboardNotification(WhiteboardStatus.INSTANTIATED);
setNewWhiteboardOpen(store);
return next(action);
}
@ -146,3 +132,29 @@ function raiseWhiteboardNotification(status: WhiteboardStatus) {
}
}
/**
* Sets a new whiteboard open.
*
* @param {IStore} store - The redux store.
* @returns {Promise}
*/
async function setNewWhiteboardOpen(store: IStore) {
const { dispatch, getState } = store;
const collabLinkData = await generateCollaborationLinkData();
const state = getState();
const conference = getCurrentConference(state);
const collabServerUrl = generateCollabServerUrl(state);
const roomId = getCurrentRoomId(state);
const collabData = {
collabDetails: {
roomId,
roomKey: collabLinkData.roomKey
},
collabServerUrl
};
focusWhiteboard(store);
dispatch(setupWhiteboard(collabData));
conference?.getMetadataHandler().setMetadata(WHITEBOARD_ID, collabData);
raiseWhiteboardNotification(WhiteboardStatus.INSTANTIATED);
}

Loading…
Cancel
Save