Skip to content

Commit

Permalink
Introduces new feature videosipgw.
Browse files Browse the repository at this point in the history
  • Loading branch information
damencho committed Nov 21, 2017
1 parent b91d588 commit ec1d564
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 33 deletions.
13 changes: 13 additions & 0 deletions lang/main.json
Expand Up @@ -439,6 +439,19 @@
"streamIdHelp": "Where do I find this?",
"unavailableTitle": "Live Streaming unavailable"
},
"videoSIPGW":
{
"busy": "We're working on freeing resources. Please try again in a few minutes.",
"busyTitle": "The Room service is currently busy",
"errorInvite": "Conference not established yet. Please try again later.",
"errorInviteTitle": "Error inviting room",
"errorAlreadyInvited": "__displayName__ already invited",
"errorInviteFailedTitle": "Inviting __displayName__ failed",
"errorInviteFailed": "We're working on resolving the issue. Please try again later.",
"pending": "__displayName__ has been invited",
"serviceName": "Room service",
"unavailableTitle": "Room service unavailable"
},
"speakerStats":
{
"hours": "__count__h",
Expand Down
1 change: 1 addition & 0 deletions react/features/base/lib-jitsi-meet/index.js
Expand Up @@ -16,6 +16,7 @@ export const JitsiMediaDevicesEvents = JitsiMeetJS.events.mediaDevices;
export const JitsiParticipantConnectionStatus
= JitsiMeetJS.constants.participantConnectionStatus;
export const JitsiRecordingStatus = JitsiMeetJS.constants.recordingStatus;
export const JitsiSIPVideoGWStatus = JitsiMeetJS.constants.sipVideoGW;
export const JitsiTrackErrors = JitsiMeetJS.errors.track;
export const JitsiTrackEvents = JitsiMeetJS.events.track;

Expand Down
20 changes: 14 additions & 6 deletions react/features/invite/components/AddPeopleDialog.web.js
Expand Up @@ -12,7 +12,8 @@ import { Dialog, hideDialog } from '../../base/dialog';
import { translate } from '../../base/i18n';
import { MultiSelectAutocomplete } from '../../base/react';

import { invitePeople, inviteRooms, searchPeople } from '../functions';
import { invitePeople, searchPeople } from '../functions';
import { inviteRooms } from '../../videosipgw';

declare var interfaceConfig: Object;

Expand Down Expand Up @@ -62,6 +63,11 @@ class AddPeopleDialog extends Component<*, *> {
*/
hideDialog: PropTypes.func,

/**
* Used to invite video rooms.
*/
inviteRooms: PropTypes.func,

/**
* Invoked to obtain translated strings.
*/
Expand Down Expand Up @@ -215,10 +221,10 @@ class AddPeopleDialog extends Component<*, *> {
});

this.props._conference
&& inviteRooms(
this.props._conference,
this.state.inviteItems.filter(
i => i.type === 'videosipgw'));
&& this.props.inviteRooms(
this.props._conference,
this.state.inviteItems.filter(
i => i.type === 'videosipgw'));

invitePeople(
this.props._inviteServiceUrl,
Expand Down Expand Up @@ -356,5 +362,7 @@ function _mapStateToProps(state) {
};
}

export default translate(connect(_mapStateToProps, { hideDialog })(
export default translate(connect(_mapStateToProps, {
hideDialog,
inviteRooms })(
AddPeopleDialog));
27 changes: 0 additions & 27 deletions react/features/invite/functions.js
Expand Up @@ -48,33 +48,6 @@ export function invitePeople( // eslint-disable-line max-params
});
}

/**
* Invites room participants to the conference through the SIP Jibri service.
*
* @param {JitsiMeetConference} conference - The conference to which the rooms
* will be invited to.
* @param {Immutable.List} rooms - The list of the "videosipgw" type items to
* invite.
* @returns {void}
*/
export function inviteRooms(
conference: { createVideoSIPGWSession: Function },
rooms: Object) {
for (const room of rooms) {
const { id: sipAddress, name: displayName } = room;

if (sipAddress && displayName) {
const newSession
= conference.createVideoSIPGWSession(sipAddress, displayName);

newSession.start();
} else {
console.error(
`No display name or sip number for ${JSON.stringify(room)}`);
}
}
}

/**
* Indicates if an invite option is enabled in the configuration.
*
Expand Down
23 changes: 23 additions & 0 deletions react/features/videosipgw/actionTypes.js
@@ -0,0 +1,23 @@
/**
* The type of (redux) action which signals that sip GW service change its
* availability status.
*
* {
* type: SIP_GW_AVAILABILITY_CHANGED,
* status: string
* }
*/
export const SIP_GW_AVAILABILITY_CHANGED
= Symbol('SIP_GW_AVAILABILITY_CHANGED');

/**
* The type of the action which signals to invite room participants to the
* conference through the SIP Jibri service.
*
* {
* type: SIP_GW_INVITE_ROOMS,
* conference: JitsiConference,
* rooms: {Immutable.List}
* }
*/
export const SIP_GW_INVITE_ROOMS = Symbol('SIP_GW_INVITE_ROOMS');
22 changes: 22 additions & 0 deletions react/features/videosipgw/actions.js
@@ -0,0 +1,22 @@
/* @flow */

import { SIP_GW_INVITE_ROOMS } from './actionTypes';

/**
* Invites room participants to the conference through the SIP Jibri service.
*
* @param {JitsiMeetConference} conference - The conference to which the rooms
* will be invited to.
* @param {Immutable.List} rooms - The list of the "videosipgw" type items to
* invite.
* @returns {void}
*/
export function inviteRooms(
conference: Object,
rooms: Object) {
return {
type: SIP_GW_INVITE_ROOMS,
conference,
rooms
};
}
4 changes: 4 additions & 0 deletions react/features/videosipgw/index.js
@@ -0,0 +1,4 @@
export * from './actions';

import './middleware';
import './reducer';
182 changes: 182 additions & 0 deletions react/features/videosipgw/middleware.js
@@ -0,0 +1,182 @@
/* @flow */

import Logger from 'jitsi-meet-logger';
import { CONFERENCE_WILL_JOIN } from '../base/conference';
import {
SIP_GW_AVAILABILITY_CHANGED,
SIP_GW_INVITE_ROOMS
} from './actionTypes';
import {
JitsiConferenceEvents,
JitsiSIPVideoGWStatus
} from '../base/lib-jitsi-meet';
import { MiddlewareRegistry } from '../base/redux';
import {
Notification,
showErrorNotification,
showNotification,
showWarningNotification
} from '../notifications';

const logger = Logger.getLogger(__filename);

/**
* Middleware that captures conference video sip gw events and stores
* the global sip gw availability in redux or show appropriate notification
* for sip gw sessions.
* Captures invitation actions that create sip gw sessions or display
* appropriate error/warning notifications.
*
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const result = next(action);

switch (action.type) {
case CONFERENCE_WILL_JOIN: {
const conference = getState()['features/base/conference'].joining;

conference.on(
JitsiConferenceEvents.VIDEO_SIP_GW_AVAILABILITY_CHANGED,
(...args) => dispatch(_availabilityChanged(...args)));
conference.on(
JitsiConferenceEvents.VIDEO_SIP_GW_SESSION_STATE_CHANGED,
event => {
const toDispatch = _sessionStateChanged(event);

// sessionStateChanged can decide there is nothing to dispatch
if (toDispatch) {
dispatch(toDispatch);
}
});

break;
}
case SIP_GW_INVITE_ROOMS: {
const { status } = getState()['features/videosipgw'];

if (status === JitsiSIPVideoGWStatus.STATUS_UNDEFINED) {
dispatch(showErrorNotification({
descriptionKey: 'recording.unavailable',
descriptionArguments: {
serviceName: '$t(videoSIPGW.serviceName)'
},
titleKey: 'videoSIPGW.unavailableTitle'
}));

return;
} else if (status === JitsiSIPVideoGWStatus.STATUS_BUSY) {
dispatch(showWarningNotification({
descriptionKey: 'videoSIPGW.busy',
titleKey: 'videoSIPGW.busyTitle'
}));

return;
} else if (status !== JitsiSIPVideoGWStatus.STATUS_AVAILABLE) {
logger.error(`Unknown sip videogw status ${status}`);

return;
}

for (const room of action.rooms) {
const { id: sipAddress, name: displayName } = room;

if (sipAddress && displayName) {
const newSession = action.conference
.createVideoSIPGWSession(sipAddress, displayName);

if (newSession instanceof Error) {
const e = newSession;

if (e) {
switch (e.message) {
case JitsiSIPVideoGWStatus.ERROR_NO_CONNECTION: {
dispatch(showErrorNotification({
descriptionKey: 'videoSIPGW.errorInvite',
titleKey: 'videoSIPGW.errorInviteTitle'
}));

return;
}
case JitsiSIPVideoGWStatus.ERROR_SESSION_EXISTS: {
dispatch(showWarningNotification({
titleKey: 'videoSIPGW.errorAlreadyInvited',
titleArguments: { displayName }
}));

return;
}
}
}
logger.error(
'Unknown error trying to create sip videogw session',
e);

return;
}

newSession.start();
} else {
logger.error(`No display name or sip number for ${
JSON.stringify(room)}`);
}
}
}
}

return result;
});

/**
* Signals that sip gw availability had changed.
*
* @param {string} status - The new status of the service.
* @returns {{
* type: SIP_GW_AVAILABILITY_CHANGED,
* status: string
* }}
* @private
*/
function _availabilityChanged(status: string) {
return {
type: SIP_GW_AVAILABILITY_CHANGED,
status
};
}

/**
* Signals that a session we created has a change in its status.
*
* @param {string} event - The event describing the session state change.
* @returns {{
* type: SHOW_NOTIFICATION
* }}|null
* @private
*/
function _sessionStateChanged(
event: Object) {
switch (event.newState) {
case JitsiSIPVideoGWStatus.STATE_PENDING: {
return showNotification(
Notification, {
titleKey: 'videoSIPGW.pending',
titleArguments: {
displayName: event.displayName
}
}, 2000);
}
case JitsiSIPVideoGWStatus.STATE_FAILED: {
return showErrorNotification({
titleKey: 'videoSIPGW.errorInviteFailedTitle',
titleArguments: {
displayName: event.displayName
},
descriptionKey: 'videoSIPGW.errorInviteFailed'
});
}
}

// nothing to show
return null;
}
17 changes: 17 additions & 0 deletions react/features/videosipgw/reducer.js
@@ -0,0 +1,17 @@
import { ReducerRegistry } from '../base/redux';

import { SIP_GW_AVAILABILITY_CHANGED } from './actionTypes';

ReducerRegistry.register(
'features/videosipgw', (state = [], action) => {
switch (action.type) {
case SIP_GW_AVAILABILITY_CHANGED: {
return {
...state,
status: action.status
};
}
}

return state;
});

0 comments on commit ec1d564

Please sign in to comment.