浏览代码

VideoSIPGW updates (#2201)

* Adds initial documentation for sipgw jibri.

Also explains enabling the people search service and the request/response that are made around sipgw jibri service.

* Fixes add people dialog to invite users and rooms.

No invitation is sent when there is nobody to invite.

* Reuse some recording strings, by using arguments.

* Make sure web also dispatches CONFERENCE_WILL_JOIN.

* Introduces new feature videosipgw.

* Fixes lint errors.

* Renames methods to use people, chatRooms and videoRooms.

* Updates to latest lib-jitsi-meet (dc3397b18b).
master
Дамян Минков 7 年前
父节点
当前提交
7b1b873b6e

+ 2
- 0
conference.js 查看文件

25
     conferenceFailed,
25
     conferenceFailed,
26
     conferenceJoined,
26
     conferenceJoined,
27
     conferenceLeft,
27
     conferenceLeft,
28
+    conferenceWillJoin,
28
     dataChannelOpened,
29
     dataChannelOpened,
29
     EMAIL_COMMAND,
30
     EMAIL_COMMAND,
30
     lockStateChanged,
31
     lockStateChanged,
1249
             = connection.initJitsiConference(
1250
             = connection.initJitsiConference(
1250
                 APP.conference.roomName,
1251
                 APP.conference.roomName,
1251
                 this._getConferenceOptions());
1252
                 this._getConferenceOptions());
1253
+        APP.store.dispatch(conferenceWillJoin(room));
1252
         this._setLocalAudioVideoStreams(localTracks);
1254
         this._setLocalAudioVideoStreams(localTracks);
1253
         this._room = room; // FIXME do not use this
1255
         this._room = room; // FIXME do not use this
1254
 
1256
 

+ 57
- 0
doc/sipgw-config.md 查看文件

1
+# Configuring sipgw jibri with jitsi-meet
2
+
3
+This document describes how you can configure jitsi-meet to use sipgw jibri and enable rooms in 'Add people dialog'
4
+You will need a working deployment of jibri configured to use a regular sip video device, for more info check out the [jibri documentation](https://github.com/jitsi/jibri/blob/master/README.md).
5
+
6
+This feature is available for non-guests of the system, so this relies on setting in config.js ``enableUserRolesBasedOnToken: true`` and providing a jwt token when accessing the conference.
7
+
8
+* Jicofo configuration:
9
+edit /etc/jitsi/jicofo/sip-communicator.properties (or similar), set the appropriate MUC to look for the Jibri Controllers. This should be the same MUC as is referenced in jibri's config.json file. Restart Jicofo after setting this property.
10
+
11
+```
12
+  org.jitsi.jicofo.jibri.SIP_BREWERY=TheSipBrewery@conference.yourdomain.com
13
+ ```
14
+
15
+* Jitsi Meet configuration:
16
+ - config.js: add 
17
+```
18
+  enableUserRolesBasedOnToken: true,
19
+  peopleSearchQueryTypes: ['conferenceRooms'],
20
+  peopleSearchUrl: 'https://api.yourdomain.com/testpath/searchpeople',
21
+```
22
+ - interface_config.js:
23
+```
24
+  ADD_PEOPLE_APP_NAME: 'Jitsi'
25
+```
26
+
27
+The combination of the above settings and providing a jwt token will enable a button under invite option which will show the dialog 'Add people'.
28
+
29
+## People search service
30
+
31
+When searching in the dialog, a request for results is made to the `peopleSearchUrl` service.
32
+
33
+The request is in the following format:
34
+```
35
+https://api.yourdomain.com/testpath/searchpeople?query=testroomname&queryTypes=[%22conferenceRooms%22]&jwt=somejwt
36
+```
37
+The parameters are:
38
+ - query - The text entered by the user.
39
+ - queryTypes - What type of results we want people, rooms, conferenceRooms. This is the value from config.js `peopleSearchQueryTypes`
40
+ - jwt - The token used by the user to access the conference.
41
+
42
+The response of the service is a json in the following format:
43
+```
44
+[
45
+   {
46
+       "id": "address@sip.domain.com",
47
+       "name": "Some room name",
48
+       "type": "videosipgw"
49
+   },
50
+  {
51
+      "id": "address2@sip.domain.com",
52
+      "name": "Some room name2",
53
+      "type": "videosipgw"
54
+  }
55
+]
56
+```
57
+Type should be `videosipgw`, `name` is the name shown to the user and `id` is the sip address to be called by the sipgw jibri.

+ 16
- 2
lang/main.json 查看文件

420
         "off": "Recording stopped",
420
         "off": "Recording stopped",
421
         "on": "Recording",
421
         "on": "Recording",
422
         "pending": "Recording waiting for a member to join...",
422
         "pending": "Recording waiting for a member to join...",
423
-        "unavailable": "Oops! The recording service is currently unavailable. We're working on resolving the issue. Please try again later.",
423
+        "serviceName": "Recording service",
424
+        "unavailable": "Oops! The __serviceName__ is currently unavailable. We're working on resolving the issue. Please try again later.",
424
         "unavailableTitle": "Recording unavailable"
425
         "unavailableTitle": "Recording unavailable"
425
     },
426
     },
426
     "liveStreaming":
427
     "liveStreaming":
433
         "off": "Live Streaming stopped",
434
         "off": "Live Streaming stopped",
434
         "on": "Live Streaming",
435
         "on": "Live Streaming",
435
         "pending": "Starting Live Stream...",
436
         "pending": "Starting Live Stream...",
437
+        "serviceName": "Live Streaming service",
436
         "streamIdRequired": "Please fill in the stream id in order to launch the Live Streaming.",
438
         "streamIdRequired": "Please fill in the stream id in order to launch the Live Streaming.",
437
         "streamIdHelp": "Where do I find this?",
439
         "streamIdHelp": "Where do I find this?",
438
-        "unavailable": "Oops! The Live Streaming service is currently unavailable. We're working on resolving the issue. Please try again later.",
439
         "unavailableTitle": "Live Streaming unavailable"
440
         "unavailableTitle": "Live Streaming unavailable"
440
     },
441
     },
442
+    "videoSIPGW":
443
+    {
444
+        "busy": "We're working on freeing resources. Please try again in a few minutes.",
445
+        "busyTitle": "The Room service is currently busy",
446
+        "errorInvite": "Conference not established yet. Please try again later.",
447
+        "errorInviteTitle": "Error inviting room",
448
+        "errorAlreadyInvited": "__displayName__ already invited",
449
+        "errorInviteFailedTitle": "Inviting __displayName__ failed",
450
+        "errorInviteFailed": "We're working on resolving the issue. Please try again later.",
451
+        "pending": "__displayName__ has been invited",
452
+        "serviceName": "Room service",
453
+        "unavailableTitle": "Room service unavailable"
454
+    },
441
     "speakerStats":
455
     "speakerStats":
442
     {
456
     {
443
         "hours": "__count__h",
457
         "hours": "__count__h",

+ 5
- 1
modules/UI/recording/Recording.js 查看文件

51
     recordingPendingKey: 'recording.pending',
51
     recordingPendingKey: 'recording.pending',
52
     recordingTitle: 'dialog.recording',
52
     recordingTitle: 'dialog.recording',
53
     recordingUnavailable: 'recording.unavailable',
53
     recordingUnavailable: 'recording.unavailable',
54
+    recordingUnavailableParams: '$t(recording.serviceName)',
54
     recordingUnavailableTitle: 'recording.unavailableTitle'
55
     recordingUnavailableTitle: 'recording.unavailableTitle'
55
 };
56
 };
56
 
57
 
71
     recordingOnKey: 'liveStreaming.on',
72
     recordingOnKey: 'liveStreaming.on',
72
     recordingPendingKey: 'liveStreaming.pending',
73
     recordingPendingKey: 'liveStreaming.pending',
73
     recordingTitle: 'dialog.liveStreaming',
74
     recordingTitle: 'dialog.liveStreaming',
74
-    recordingUnavailable: 'liveStreaming.unavailable',
75
+    recordingUnavailable: 'recording.unavailable',
76
+    recordingUnavailableParams: '$t(liveStreaming.serviceName)',
75
     recordingUnavailableTitle: 'liveStreaming.unavailableTitle'
77
     recordingUnavailableTitle: 'liveStreaming.unavailableTitle'
76
 };
78
 };
77
 
79
 
531
         default: {
533
         default: {
532
             APP.UI.messageHandler.showError({
534
             APP.UI.messageHandler.showError({
533
                 descriptionKey: this.recordingUnavailable,
535
                 descriptionKey: this.recordingUnavailable,
536
+                descriptionArguments: {
537
+                    serviceName: this.recordingUnavailableParams },
534
                 titleKey: this.recordingUnavailableTitle
538
                 titleKey: this.recordingUnavailableTitle
535
             });
539
             });
536
         }
540
         }

+ 1
- 1
package-lock.json 查看文件

10991
       }
10991
       }
10992
     },
10992
     },
10993
     "lib-jitsi-meet": {
10993
     "lib-jitsi-meet": {
10994
-      "version": "github:jitsi/lib-jitsi-meet#21b07deabccd0b28b6d8eacaa6eee613e018b482",
10994
+      "version": "github:jitsi/lib-jitsi-meet#dc3397b18b3c3c41ee8d17224f9af9318b844c43",
10995
       "requires": {
10995
       "requires": {
10996
         "async": "0.9.0",
10996
         "async": "0.9.0",
10997
         "current-executing-script": "0.1.3",
10997
         "current-executing-script": "0.1.3",

+ 1
- 1
package.json 查看文件

46
     "js-md5": "0.6.1",
46
     "js-md5": "0.6.1",
47
     "jssha": "2.2.0",
47
     "jssha": "2.2.0",
48
     "jwt-decode": "2.2.0",
48
     "jwt-decode": "2.2.0",
49
-    "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#21b07deabccd0b28b6d8eacaa6eee613e018b482",
49
+    "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#dc3397b18b3c3c41ee8d17224f9af9318b844c43",
50
     "lodash": "4.17.4",
50
     "lodash": "4.17.4",
51
     "nuclear-js": "1.4.0",
51
     "nuclear-js": "1.4.0",
52
     "postis": "2.2.0",
52
     "postis": "2.2.0",

+ 19
- 4
react/features/base/conference/actions.js 查看文件

229
             _addLocalTracksToConference(conference, localTracks);
229
             _addLocalTracksToConference(conference, localTracks);
230
         }
230
         }
231
 
231
 
232
-        dispatch({
233
-            type: CONFERENCE_WILL_JOIN,
234
-            conference
235
-        });
232
+        dispatch(conferenceWillJoin(conference));
233
+    };
234
+}
235
+
236
+/**
237
+ * Signals the intention of the application to have the local participant
238
+ * join the specified conference.
239
+ *
240
+ * @param {JitsiConference} conference - The {@code JitsiConference} instance
241
+ * the local participant will (try to) join.
242
+ * @returns {{
243
+ *     type: CONFERENCE_WILL_JOIN,
244
+ *     conference: JitsiConference
245
+ * }}
246
+ */
247
+export function conferenceWillJoin(conference: Object) {
248
+    return {
249
+        type: CONFERENCE_WILL_JOIN,
250
+        conference
236
     };
251
     };
237
 }
252
 }
238
 
253
 

+ 1
- 0
react/features/base/lib-jitsi-meet/index.js 查看文件

16
 export const JitsiParticipantConnectionStatus
16
 export const JitsiParticipantConnectionStatus
17
     = JitsiMeetJS.constants.participantConnectionStatus;
17
     = JitsiMeetJS.constants.participantConnectionStatus;
18
 export const JitsiRecordingStatus = JitsiMeetJS.constants.recordingStatus;
18
 export const JitsiRecordingStatus = JitsiMeetJS.constants.recordingStatus;
19
+export const JitsiSIPVideoGWStatus = JitsiMeetJS.constants.sipVideoGW;
19
 export const JitsiTrackErrors = JitsiMeetJS.errors.track;
20
 export const JitsiTrackErrors = JitsiMeetJS.errors.track;
20
 export const JitsiTrackEvents = JitsiMeetJS.events.track;
21
 export const JitsiTrackEvents = JitsiMeetJS.events.track;
21
 
22
 

+ 18
- 9
react/features/invite/components/AddPeopleDialog.web.js 查看文件

12
 import { translate } from '../../base/i18n';
12
 import { translate } from '../../base/i18n';
13
 import { MultiSelectAutocomplete } from '../../base/react';
13
 import { MultiSelectAutocomplete } from '../../base/react';
14
 
14
 
15
-import { invitePeople, inviteRooms, searchPeople } from '../functions';
15
+import { invitePeopleAndChatRooms, searchDirectory } from '../functions';
16
+import { inviteVideoRooms } from '../../videosipgw';
16
 
17
 
17
 declare var interfaceConfig: Object;
18
 declare var interfaceConfig: Object;
18
 
19
 
62
          */
63
          */
63
         hideDialog: PropTypes.func,
64
         hideDialog: PropTypes.func,
64
 
65
 
66
+        /**
67
+         * Used to invite video rooms.
68
+         */
69
+        inviteVideoRooms: PropTypes.func,
70
+
65
         /**
71
         /**
66
          * Invoked to obtain translated strings.
72
          * Invoked to obtain translated strings.
67
          */
73
          */
79
             } = this.props; // eslint-disable-line no-invalid-this
85
             } = this.props; // eslint-disable-line no-invalid-this
80
 
86
 
81
             return (
87
             return (
82
-                searchPeople(
88
+                searchDirectory(
83
                     _peopleSearchUrl,
89
                     _peopleSearchUrl,
84
                     _jwt,
90
                     _jwt,
85
                     text,
91
                     text,
215
             });
221
             });
216
 
222
 
217
             this.props._conference
223
             this.props._conference
218
-                && inviteRooms(
219
-                    this.props._conference,
220
-                    this.state.inviteItems.filter(
221
-                        i => i.type === 'videosipgw'));
224
+                && this.props.inviteVideoRooms(
225
+                        this.props._conference,
226
+                        this.state.inviteItems.filter(
227
+                            i => i.type === 'videosipgw'));
222
 
228
 
223
-            invitePeople(
229
+            invitePeopleAndChatRooms(
224
                 this.props._inviteServiceUrl,
230
                 this.props._inviteServiceUrl,
225
                 this.props._inviteUrl,
231
                 this.props._inviteUrl,
226
                 this.props._jwt,
232
                 this.props._jwt,
227
-                this.state.inviteItems.filter(i => i.type === 'user'))
233
+                this.state.inviteItems.filter(
234
+                    i => i.type === 'user' || i.type === 'room'))
228
             .then(
235
             .then(
229
                 /* onFulfilled */ () => {
236
                 /* onFulfilled */ () => {
230
                     this.setState({
237
                     this.setState({
355
     };
362
     };
356
 }
363
 }
357
 
364
 
358
-export default translate(connect(_mapStateToProps, { hideDialog })(
365
+export default translate(connect(_mapStateToProps, {
366
+    hideDialog,
367
+    inviteVideoRooms })(
359
     AddPeopleDialog));
368
     AddPeopleDialog));

+ 10
- 32
react/features/invite/functions.js 查看文件

22
  * invitation.
22
  * invitation.
23
  * @param {string} inviteUrl - The url to the conference.
23
  * @param {string} inviteUrl - The url to the conference.
24
  * @param {string} jwt - The jwt token to pass to the search service.
24
  * @param {string} jwt - The jwt token to pass to the search service.
25
- * @param {Immutable.List} people - The list of the "user" type items to invite.
25
+ * @param {Immutable.List} inviteItems - The list of the "user" or "room"
26
+ * type items to invite.
26
  * @returns {Promise} - The promise created by the request.
27
  * @returns {Promise} - The promise created by the request.
27
  */
28
  */
28
-export function invitePeople( // eslint-disable-line max-params
29
+export function invitePeopleAndChatRooms( // eslint-disable-line max-params
29
         inviteServiceUrl: string,
30
         inviteServiceUrl: string,
30
         inviteUrl: string,
31
         inviteUrl: string,
31
         jwt: string,
32
         jwt: string,
32
-        people: Object) {
33
+        inviteItems: Object) {
34
+    if (!inviteItems || inviteItems.length === 0) {
35
+        return Promise.resolve();
36
+    }
37
+
33
     return new Promise((resolve, reject) => {
38
     return new Promise((resolve, reject) => {
34
         $.post(
39
         $.post(
35
                 `${inviteServiceUrl}?token=${jwt}`,
40
                 `${inviteServiceUrl}?token=${jwt}`,
36
                 JSON.stringify({
41
                 JSON.stringify({
37
-                    'invited': people,
42
+                    'invited': inviteItems,
38
                     'url': inviteUrl
43
                     'url': inviteUrl
39
                 }),
44
                 }),
40
                 resolve,
45
                 resolve,
43
     });
48
     });
44
 }
49
 }
45
 
50
 
46
-/**
47
- * Invites room participants to the conference through the SIP Jibri service.
48
- *
49
- * @param {JitsiMeetConference} conference - The conference to which the rooms
50
- * will be invited to.
51
- * @param {Immutable.List} rooms - The list of the "videosipgw" type items to
52
- * invite.
53
- * @returns {void}
54
- */
55
-export function inviteRooms(
56
-        conference: { createVideoSIPGWSession: Function },
57
-        rooms: Object) {
58
-    for (const room of rooms) {
59
-        const { id: sipAddress, name: displayName } = room;
60
-
61
-        if (sipAddress && displayName) {
62
-            const newSession
63
-                = conference.createVideoSIPGWSession(sipAddress, displayName);
64
-
65
-            newSession.start();
66
-        } else {
67
-            console.error(
68
-                `No display name or sip number for ${JSON.stringify(room)}`);
69
-        }
70
-    }
71
-}
72
-
73
 /**
51
 /**
74
  * Indicates if an invite option is enabled in the configuration.
52
  * Indicates if an invite option is enabled in the configuration.
75
  *
53
  *
92
  * executed - "conferenceRooms" | "user" | "room".
70
  * executed - "conferenceRooms" | "user" | "room".
93
  * @returns {Promise} - The promise created by the request.
71
  * @returns {Promise} - The promise created by the request.
94
  */
72
  */
95
-export function searchPeople( // eslint-disable-line max-params
73
+export function searchDirectory( // eslint-disable-line max-params
96
         serviceUrl: string,
74
         serviceUrl: string,
97
         jwt: string,
75
         jwt: string,
98
         text: string,
76
         text: string,

+ 0
- 3
react/features/notifications/components/Notification.web.js 查看文件

145
     render() {
145
     render() {
146
         const {
146
         const {
147
             appearance,
147
             appearance,
148
-            description,
149
-            descriptionArguments,
150
-            descriptionKey,
151
             hideErrorSupportLink,
148
             hideErrorSupportLink,
152
             isDismissAllowed,
149
             isDismissAllowed,
153
             onDismissed,
150
             onDismissed,

+ 23
- 0
react/features/videosipgw/actionTypes.js 查看文件

1
+/**
2
+ * The type of (redux) action which signals that sip GW service change its
3
+ * availability status.
4
+ *
5
+ * {
6
+ *     type: SIP_GW_AVAILABILITY_CHANGED,
7
+ *     status: string
8
+ * }
9
+ */
10
+export const SIP_GW_AVAILABILITY_CHANGED
11
+    = Symbol('SIP_GW_AVAILABILITY_CHANGED');
12
+
13
+/**
14
+ * The type of the action which signals to invite room participants to the
15
+ * conference through the SIP Jibri service.
16
+ *
17
+ * {
18
+ *     type: SIP_GW_INVITE_ROOMS,
19
+ *     conference: JitsiConference,
20
+ *     rooms: {Immutable.List}
21
+ * }
22
+ */
23
+export const SIP_GW_INVITE_ROOMS = Symbol('SIP_GW_INVITE_ROOMS');

+ 22
- 0
react/features/videosipgw/actions.js 查看文件

1
+/* @flow */
2
+
3
+import { SIP_GW_INVITE_ROOMS } from './actionTypes';
4
+
5
+/**
6
+ * Invites room participants to the conference through the SIP Jibri service.
7
+ *
8
+ * @param {JitsiMeetConference} conference - The conference to which the rooms
9
+ * will be invited to.
10
+ * @param {Immutable.List} rooms - The list of the "videosipgw" type items to
11
+ * invite.
12
+ * @returns {void}
13
+ */
14
+export function inviteVideoRooms(
15
+        conference: Object,
16
+        rooms: Object) {
17
+    return {
18
+        type: SIP_GW_INVITE_ROOMS,
19
+        conference,
20
+        rooms
21
+    };
22
+}

+ 4
- 0
react/features/videosipgw/index.js 查看文件

1
+export * from './actions';
2
+
3
+import './middleware';
4
+import './reducer';

+ 182
- 0
react/features/videosipgw/middleware.js 查看文件

1
+/* @flow */
2
+
3
+import Logger from 'jitsi-meet-logger';
4
+import { CONFERENCE_WILL_JOIN } from '../base/conference';
5
+import {
6
+    SIP_GW_AVAILABILITY_CHANGED,
7
+    SIP_GW_INVITE_ROOMS
8
+} from './actionTypes';
9
+import {
10
+    JitsiConferenceEvents,
11
+    JitsiSIPVideoGWStatus
12
+} from '../base/lib-jitsi-meet';
13
+import { MiddlewareRegistry } from '../base/redux';
14
+import {
15
+    Notification,
16
+    showErrorNotification,
17
+    showNotification,
18
+    showWarningNotification
19
+} from '../notifications';
20
+
21
+const logger = Logger.getLogger(__filename);
22
+
23
+/**
24
+ * Middleware that captures conference video sip gw events and stores
25
+ * the global sip gw availability in redux or show appropriate notification
26
+ * for sip gw sessions.
27
+ * Captures invitation actions that create sip gw sessions or display
28
+ * appropriate error/warning notifications.
29
+ *
30
+ * @param {Store} store - The redux store.
31
+ * @returns {Function}
32
+ */
33
+MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
34
+    const result = next(action);
35
+
36
+    switch (action.type) {
37
+    case CONFERENCE_WILL_JOIN: {
38
+        const conference = getState()['features/base/conference'].joining;
39
+
40
+        conference.on(
41
+            JitsiConferenceEvents.VIDEO_SIP_GW_AVAILABILITY_CHANGED,
42
+            (...args) => dispatch(_availabilityChanged(...args)));
43
+        conference.on(
44
+            JitsiConferenceEvents.VIDEO_SIP_GW_SESSION_STATE_CHANGED,
45
+            event => {
46
+                const toDispatch = _sessionStateChanged(event);
47
+
48
+                // sessionStateChanged can decide there is nothing to dispatch
49
+                if (toDispatch) {
50
+                    dispatch(toDispatch);
51
+                }
52
+            });
53
+
54
+        break;
55
+    }
56
+    case SIP_GW_INVITE_ROOMS: {
57
+        const { status } = getState()['features/videosipgw'];
58
+
59
+        if (status === JitsiSIPVideoGWStatus.STATUS_UNDEFINED) {
60
+            dispatch(showErrorNotification({
61
+                descriptionKey: 'recording.unavailable',
62
+                descriptionArguments: {
63
+                    serviceName: '$t(videoSIPGW.serviceName)'
64
+                },
65
+                titleKey: 'videoSIPGW.unavailableTitle'
66
+            }));
67
+
68
+            return;
69
+        } else if (status === JitsiSIPVideoGWStatus.STATUS_BUSY) {
70
+            dispatch(showWarningNotification({
71
+                descriptionKey: 'videoSIPGW.busy',
72
+                titleKey: 'videoSIPGW.busyTitle'
73
+            }));
74
+
75
+            return;
76
+        } else if (status !== JitsiSIPVideoGWStatus.STATUS_AVAILABLE) {
77
+            logger.error(`Unknown sip videogw status ${status}`);
78
+
79
+            return;
80
+        }
81
+
82
+        for (const room of action.rooms) {
83
+            const { id: sipAddress, name: displayName } = room;
84
+
85
+            if (sipAddress && displayName) {
86
+                const newSession = action.conference
87
+                    .createVideoSIPGWSession(sipAddress, displayName);
88
+
89
+                if (newSession instanceof Error) {
90
+                    const e = newSession;
91
+
92
+                    if (e) {
93
+                        switch (e.message) {
94
+                        case JitsiSIPVideoGWStatus.ERROR_NO_CONNECTION: {
95
+                            dispatch(showErrorNotification({
96
+                                descriptionKey: 'videoSIPGW.errorInvite',
97
+                                titleKey: 'videoSIPGW.errorInviteTitle'
98
+                            }));
99
+
100
+                            return;
101
+                        }
102
+                        case JitsiSIPVideoGWStatus.ERROR_SESSION_EXISTS: {
103
+                            dispatch(showWarningNotification({
104
+                                titleKey: 'videoSIPGW.errorAlreadyInvited',
105
+                                titleArguments: { displayName }
106
+                            }));
107
+
108
+                            return;
109
+                        }
110
+                        }
111
+                    }
112
+                    logger.error(
113
+                        'Unknown error trying to create sip videogw session',
114
+                        e);
115
+
116
+                    return;
117
+                }
118
+
119
+                newSession.start();
120
+            } else {
121
+                logger.error(`No display name or sip number for ${
122
+                    JSON.stringify(room)}`);
123
+            }
124
+        }
125
+    }
126
+    }
127
+
128
+    return result;
129
+});
130
+
131
+/**
132
+ * Signals that sip gw availability had changed.
133
+ *
134
+ * @param {string} status - The new status of the service.
135
+ * @returns {{
136
+ *     type: SIP_GW_AVAILABILITY_CHANGED,
137
+ *     status: string
138
+ * }}
139
+ * @private
140
+ */
141
+function _availabilityChanged(status: string) {
142
+    return {
143
+        type: SIP_GW_AVAILABILITY_CHANGED,
144
+        status
145
+    };
146
+}
147
+
148
+/**
149
+ * Signals that a session we created has a change in its status.
150
+ *
151
+ * @param {string} event - The event describing the session state change.
152
+ * @returns {{
153
+ *     type: SHOW_NOTIFICATION
154
+ * }}|null
155
+ * @private
156
+ */
157
+function _sessionStateChanged(
158
+        event: Object) {
159
+    switch (event.newState) {
160
+    case JitsiSIPVideoGWStatus.STATE_PENDING: {
161
+        return showNotification(
162
+            Notification, {
163
+                titleKey: 'videoSIPGW.pending',
164
+                titleArguments: {
165
+                    displayName: event.displayName
166
+                }
167
+            }, 2000);
168
+    }
169
+    case JitsiSIPVideoGWStatus.STATE_FAILED: {
170
+        return showErrorNotification({
171
+            titleKey: 'videoSIPGW.errorInviteFailedTitle',
172
+            titleArguments: {
173
+                displayName: event.displayName
174
+            },
175
+            descriptionKey: 'videoSIPGW.errorInviteFailed'
176
+        });
177
+    }
178
+    }
179
+
180
+    // nothing to show
181
+    return null;
182
+}

+ 17
- 0
react/features/videosipgw/reducer.js 查看文件

1
+import { ReducerRegistry } from '../base/redux';
2
+
3
+import { SIP_GW_AVAILABILITY_CHANGED } from './actionTypes';
4
+
5
+ReducerRegistry.register(
6
+    'features/videosipgw', (state = [], action) => {
7
+        switch (action.type) {
8
+        case SIP_GW_AVAILABILITY_CHANGED: {
9
+            return {
10
+                ...state,
11
+                status: action.status
12
+            };
13
+        }
14
+        }
15
+
16
+        return state;
17
+    });

正在加载...
取消
保存