94 lines
3.4 KiB
TypeScript
94 lines
3.4 KiB
TypeScript
import { IStore } from '../app/types';
|
|
import { CONFERENCE_FAILED, CONFERENCE_JOINED, CONFERENCE_LEFT } from '../base/conference/actionTypes';
|
|
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
|
|
|
import { REMOVE_SECOND_SCREEN, RESET_SECOND_SCREENS, SET_SECOND_SCREEN } from './actionTypes';
|
|
import { resetSecondScreens } from './actions.web';
|
|
import {
|
|
closeAllSecondScreens,
|
|
closeSecondScreenHandle,
|
|
getHandle,
|
|
handleSecondScreenOpenError,
|
|
isSecondScreenEnabled,
|
|
openOrUpdateSecondScreen,
|
|
preloadScreenDetails
|
|
} from './functions.web';
|
|
|
|
import './subscriber.web';
|
|
|
|
/**
|
|
* Middleware that reconciles the live second-screen windows with the requested
|
|
* state, tears them all down when the conference ends, and closes any that are
|
|
* still open when the main window itself goes away.
|
|
*
|
|
* @param {IStore} store - The redux store.
|
|
* @returns {Function}
|
|
*/
|
|
MiddlewareRegistry.register((store: IStore) => {
|
|
|
|
// Close every second-screen window when the main window goes away (tab closed,
|
|
// reloaded, or navigated away). The CONFERENCE_LEFT/FAILED path below only
|
|
// covers leaving the meeting from within the app; closing the main window
|
|
// directly dispatches neither, so without this the popups are orphaned on the
|
|
// second display. Closing from an unloading opener is best-effort. Skip
|
|
// event.persisted (bfcache eviction): the page may be restored via pageshow
|
|
// with the second screens still expected, so only tear down on a real unload.
|
|
window.addEventListener('pagehide', (event: PageTransitionEvent) => {
|
|
if (!event.persisted) {
|
|
closeAllSecondScreens(store);
|
|
}
|
|
});
|
|
|
|
return next => action => {
|
|
switch (action.type) {
|
|
case SET_SECOND_SCREEN: {
|
|
const result = next(action);
|
|
|
|
openOrUpdateSecondScreen(store, action.id, action.screenId)
|
|
.catch(e => handleSecondScreenOpenError(store, action.id, e));
|
|
|
|
return result;
|
|
}
|
|
case REMOVE_SECOND_SCREEN: {
|
|
|
|
// Capture the handle, then remove the entry first so the portal unmounts
|
|
// (stopping its cloned track) while the window is still open; only then
|
|
// close the window.
|
|
const handle = getHandle(store.getState(), action.id);
|
|
const result = next(action);
|
|
|
|
closeSecondScreenHandle(handle, action.id);
|
|
|
|
return result;
|
|
}
|
|
case RESET_SECOND_SCREENS:
|
|
|
|
// Same ordering: close the windows while their entries are still in state.
|
|
closeAllSecondScreens(store);
|
|
|
|
return next(action);
|
|
case CONFERENCE_JOINED: {
|
|
const result = next(action);
|
|
|
|
// Warm the screen details so an in-app trigger can open its window
|
|
// without awaiting, which is what keeps the click's user activation
|
|
// (see openOrUpdateSecondScreen). Never prompts on its own.
|
|
if (isSecondScreenEnabled(store.getState())) {
|
|
preloadScreenDetails();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
case CONFERENCE_FAILED:
|
|
case CONFERENCE_LEFT:
|
|
|
|
// Resetting closes every second-screen window (via the RESET_SECOND_SCREENS
|
|
// case above), so there is no need to close them explicitly here.
|
|
store.dispatch(resetSecondScreens());
|
|
break;
|
|
}
|
|
|
|
return next(action);
|
|
};
|
|
});
|