jiti-meet/react/features/chat/middleware.js

151 lines
4.8 KiB
JavaScript

// @flow
import UIUtil from '../../../modules/UI/util/UIUtil';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
import {
CONFERENCE_JOINED,
getCurrentConference
} from '../base/conference';
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import {
getParticipantById,
getParticipantDisplayName
} from '../base/participants';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import { playSound, registerSound, unregisterSound } from '../base/sounds';
import { isButtonEnabled, showToolbox } from '../toolbox';
import { SEND_MESSAGE } from './actionTypes';
import { addMessage, clearMessages, toggleChat } from './actions';
import { INCOMING_MSG_SOUND_ID } from './constants';
import { INCOMING_MSG_SOUND_FILE } from './sounds';
declare var APP: Object;
declare var interfaceConfig : Object;
/**
* Implements the middleware of the chat feature.
*
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case APP_WILL_MOUNT:
store.dispatch(
registerSound(INCOMING_MSG_SOUND_ID, INCOMING_MSG_SOUND_FILE));
break;
case APP_WILL_UNMOUNT:
store.dispatch(unregisterSound(INCOMING_MSG_SOUND_ID));
break;
case CONFERENCE_JOINED:
_addChatMsgListener(action.conference, store);
break;
case SEND_MESSAGE: {
const { conference } = store.getState()['features/base/conference'];
if (conference) {
const escapedMessage = UIUtil.escapeHtml(action.message);
if (typeof APP !== 'undefined') {
APP.API.notifySendingChatMessage(escapedMessage);
}
conference.sendTextMessage(escapedMessage);
}
break;
}
}
return next(action);
});
/**
* Set up state change listener to perform maintenance tasks when the conference
* is left or failed, e.g. clear messages or close the chat modal if it's left
* open.
*/
StateListenerRegistry.register(
state => getCurrentConference(state),
(conference, { dispatch, getState }, previousConference) => {
if (conference !== previousConference) {
// conference changed, left or failed...
if (getState()['features/chat'].isOpen) {
// Closes the chat if it's left open.
dispatch(toggleChat());
}
// Clear chat messages.
dispatch(clearMessages());
}
});
/**
* Registers listener for {@link JitsiConferenceEvents.MESSAGE_RECEIVED} that
* will perform various chat related activities.
*
* @param {JitsiConference} conference - The conference instance on which the
* new event listener will be registered.
* @param {Object} store - The redux store object.
* @private
* @returns {void}
*/
function _addChatMsgListener(conference, { dispatch, getState }) {
if ((typeof interfaceConfig === 'object' && interfaceConfig.filmStripOnly)
|| (typeof APP !== 'undefined' && !isButtonEnabled('chat'))
|| getState()['features/base/config'].iAmRecorder) {
// We don't register anything on web if we're in filmStripOnly mode, or
// the chat button is not enabled in interfaceConfig.
// or we are in iAmRecorder mode
return;
}
conference.on(
JitsiConferenceEvents.MESSAGE_RECEIVED,
(id, message, timestamp) => {
// Logic for all platforms:
const state = getState();
const { isOpen: isChatOpen } = state['features/chat'];
if (!isChatOpen) {
dispatch(playSound(INCOMING_MSG_SOUND_ID));
}
// Provide a default for for the case when a message is being
// backfilled for a participant that has left the conference.
const participant = getParticipantById(state, id) || {};
const displayName = getParticipantDisplayName(getState, id);
const hasRead = participant.local || isChatOpen;
const timestampToDate = timestamp
? new Date(timestamp) : new Date();
const millisecondsTimestamp = timestampToDate.getTime();
dispatch(addMessage({
displayName,
hasRead,
id,
messageType: participant.local ? 'local' : 'remote',
message,
timestamp: millisecondsTimestamp
}));
if (typeof APP !== 'undefined') {
// Logic for web only:
APP.API.notifyReceivedChatMessage({
body: message,
id,
nick: displayName,
ts: timestamp
});
dispatch(showToolbox(4000));
}
}
);
}