Преглед изворни кода

Merge pull request #8866 from jitsi/tavram/sip-invite

feat(sipcall) implement sip invite
j8
Avram Tudor пре 4 година
родитељ
комит
58b7663a97
No account linked to committer's email address

+ 1
- 0
lang/main.json Прегледај датотеку

28
         "shareInvite": "Share meeting invitation",
28
         "shareInvite": "Share meeting invitation",
29
         "shareLink": "Share the meeting link to invite others",
29
         "shareLink": "Share the meeting link to invite others",
30
         "shareStream": "Share the live streaming link",
30
         "shareStream": "Share the live streaming link",
31
+        "sip": "SIP: {{address}}",
31
         "telephone": "Telephone: {{number}}",
32
         "telephone": "Telephone: {{number}}",
32
         "title": "Invite people to this meeting",
33
         "title": "Invite people to this meeting",
33
         "yahooEmail": "Yahoo Email"
34
         "yahooEmail": "Yahoo Email"

+ 19
- 2
react/features/invite/actions.any.js Прегледај датотеку

3
 import type { Dispatch } from 'redux';
3
 import type { Dispatch } from 'redux';
4
 
4
 
5
 import { getInviteURL } from '../base/connection';
5
 import { getInviteURL } from '../base/connection';
6
-import { getParticipants } from '../base/participants';
6
+import { getLocalParticipant, getParticipants } from '../base/participants';
7
 import { inviteVideoRooms } from '../videosipgw';
7
 import { inviteVideoRooms } from '../videosipgw';
8
 
8
 
9
 import {
9
 import {
18
 import {
18
 import {
19
     getDialInConferenceID,
19
     getDialInConferenceID,
20
     getDialInNumbers,
20
     getDialInNumbers,
21
-    invitePeopleAndChatRooms
21
+    invitePeopleAndChatRooms,
22
+    inviteSipEndpoints
22
 } from './functions';
23
 } from './functions';
23
 import logger from './logger';
24
 import logger from './logger';
24
 
25
 
102
             inviteServiceCallFlowsUrl
103
             inviteServiceCallFlowsUrl
103
         } = state['features/base/config'];
104
         } = state['features/base/config'];
104
         const inviteUrl = getInviteURL(state);
105
         const inviteUrl = getInviteURL(state);
106
+        const { sipInviteUrl } = state['features/base/config'];
105
         const { jwt } = state['features/base/jwt'];
107
         const { jwt } = state['features/base/jwt'];
108
+        const { name: displayName } = getLocalParticipant(state);
106
 
109
 
107
         // First create all promises for dialing out.
110
         // First create all promises for dialing out.
108
         const phoneNumbers
111
         const phoneNumbers
164
         invitesLeftToSend
167
         invitesLeftToSend
165
             = invitesLeftToSend.filter(({ type }) => type !== 'videosipgw');
168
             = invitesLeftToSend.filter(({ type }) => type !== 'videosipgw');
166
 
169
 
170
+        const sipEndpoints
171
+            = invitesLeftToSend.filter(({ type }) => type === 'sip');
172
+
173
+        conference && inviteSipEndpoints(
174
+            sipEndpoints,
175
+            sipInviteUrl,
176
+            jwt,
177
+            conference.options.name,
178
+            displayName
179
+        );
180
+
181
+        invitesLeftToSend
182
+            = invitesLeftToSend.filter(({ type }) => type !== 'sip');
183
+
167
         return (
184
         return (
168
             Promise.all(allInvitePromises)
185
             Promise.all(allInvitePromises)
169
                 .then(() => invitesLeftToSend));
186
                 .then(() => invitesLeftToSend));

+ 14
- 5
react/features/invite/components/add-people-dialog/AbstractAddPeopleDialog.js Прегледај датотеку

12
     getInviteResultsForQuery,
12
     getInviteResultsForQuery,
13
     getInviteTypeCounts,
13
     getInviteTypeCounts,
14
     isAddPeopleEnabled,
14
     isAddPeopleEnabled,
15
-    isDialOutEnabled
15
+    isDialOutEnabled,
16
+    isSipInviteEnabled
16
 } from '../../functions';
17
 } from '../../functions';
17
 import logger from '../../logger';
18
 import logger from '../../logger';
18
 
19
 
38
      */
39
      */
39
     _dialOutEnabled: boolean,
40
     _dialOutEnabled: boolean,
40
 
41
 
42
+    /**
43
+     * Whether or not to allow sip invites.
44
+     */
45
+     _sipInviteEnabled: boolean,
46
+
41
     /**
47
     /**
42
      * The JWT token.
48
      * The JWT token.
43
      */
49
      */
96
 
102
 
97
     /**
103
     /**
98
      * Invite people and numbers to the conference. The logic works by inviting
104
      * Invite people and numbers to the conference. The logic works by inviting
99
-     * numbers, people/rooms, and videosipgw in parallel. All invitees are
105
+     * numbers, people/rooms, sip endpoints and videosipgw in parallel. All invitees are
100
      * stored in an array. As each invite succeeds, the invitee is removed
106
      * stored in an array. As each invite succeeds, the invitee is removed
101
      * from the array. After all invites finish, close the modal if there are
107
      * from the array. After all invites finish, close the modal if there are
102
      * no invites left to send. If any are left, that means an invite failed
108
      * no invites left to send. If any are left, that means an invite failed
214
             _dialOutEnabled: dialOutEnabled,
220
             _dialOutEnabled: dialOutEnabled,
215
             _jwt: jwt,
221
             _jwt: jwt,
216
             _peopleSearchQueryTypes: peopleSearchQueryTypes,
222
             _peopleSearchQueryTypes: peopleSearchQueryTypes,
217
-            _peopleSearchUrl: peopleSearchUrl
223
+            _peopleSearchUrl: peopleSearchUrl,
224
+            _sipInviteEnabled: sipInviteEnabled
218
         } = this.props;
225
         } = this.props;
219
         const options = {
226
         const options = {
220
             addPeopleEnabled,
227
             addPeopleEnabled,
222
             dialOutEnabled,
229
             dialOutEnabled,
223
             jwt,
230
             jwt,
224
             peopleSearchQueryTypes,
231
             peopleSearchQueryTypes,
225
-            peopleSearchUrl
232
+            peopleSearchUrl,
233
+            sipInviteEnabled
226
         };
234
         };
227
 
235
 
228
         return getInviteResultsForQuery(query, options);
236
         return getInviteResultsForQuery(query, options);
259
         _dialOutEnabled: isDialOutEnabled(state),
267
         _dialOutEnabled: isDialOutEnabled(state),
260
         _jwt: state['features/base/jwt'].jwt,
268
         _jwt: state['features/base/jwt'].jwt,
261
         _peopleSearchQueryTypes: peopleSearchQueryTypes,
269
         _peopleSearchQueryTypes: peopleSearchQueryTypes,
262
-        _peopleSearchUrl: peopleSearchUrl
270
+        _peopleSearchUrl: peopleSearchUrl,
271
+        _sipInviteEnabled: isSipInviteEnabled(state)
263
     };
272
     };
264
 }
273
 }

+ 18
- 2
react/features/invite/components/add-people-dialog/web/InviteContactsForm.js Прегледај датотеку

285
      */
285
      */
286
     _parseQueryResults(response = []) {
286
     _parseQueryResults(response = []) {
287
         const { t, _dialOutEnabled } = this.props;
287
         const { t, _dialOutEnabled } = this.props;
288
-        const users = response.filter(item => item.type !== 'phone');
288
+        const users = response.filter(item => item.type !== 'phone' && item.type !== 'sip');
289
         const userDisplayItems = [];
289
         const userDisplayItems = [];
290
 
290
 
291
         for (const user of users) {
291
         for (const user of users) {
348
             };
348
             };
349
         });
349
         });
350
 
350
 
351
+
352
+        const sipAddresses = response.filter(item => item.type === 'sip');
353
+
354
+        const sipDisplayItems = sipAddresses.map(sip => {
355
+            return {
356
+                filterValues: [
357
+                    sip.address
358
+                ],
359
+                content: t('addPeople.sip', { address: sip.address }),
360
+                description: '',
361
+                item: sip,
362
+                value: sip.address
363
+            };
364
+        });
365
+
351
         return [
366
         return [
352
             ...userDisplayItems,
367
             ...userDisplayItems,
353
-            ...numberDisplayItems
368
+            ...numberDisplayItems,
369
+            ...sipDisplayItems
354
         ];
370
         ];
355
     }
371
     }
356
 
372
 

+ 6
- 0
react/features/invite/constants.js Прегледај датотеку

42
  * @type {string}
42
  * @type {string}
43
  */
43
  */
44
 export const OUTGOING_CALL_START_SOUND_ID = 'OUTGOING_CALL_START_SOUND_ID';
44
 export const OUTGOING_CALL_START_SOUND_ID = 'OUTGOING_CALL_START_SOUND_ID';
45
+
46
+/**
47
+ * Regex for matching sip addresses.
48
+ */
49
+// eslint-disable-next-line max-len
50
+export const SIP_ADDRESS_REGEX = /^[a-zA-Z]+(?:([^\s>:@]+)(?::([^\s@>]+))?@)?([\w\-.]+)(?::(\d+))?((?:;[^\s=?>;]+(?:=[^\s?;]+)?)*)(?:\?(([^\s&=>]+=[^\s&=>]+)(&[^\s&=>]+=[^\s&=>]+)*))?$/;

+ 83
- 0
react/features/invite/functions.js Прегледај датотеку

10
 import { doGetJSON, parseURIString } from '../base/util';
10
 import { doGetJSON, parseURIString } from '../base/util';
11
 import { isVpaasMeeting } from '../billing-counter/functions';
11
 import { isVpaasMeeting } from '../billing-counter/functions';
12
 
12
 
13
+import { SIP_ADDRESS_REGEX } from './constants';
13
 import logger from './logger';
14
 import logger from './logger';
14
 
15
 
15
 declare var $: Function;
16
 declare var $: Function;
122
      */
123
      */
123
     peopleSearchUrl: string,
124
     peopleSearchUrl: string,
124
 
125
 
126
+    /**
127
+     * Whether or not to check sip invites.
128
+     */
129
+    sipInviteEnabled: boolean,
130
+
125
     /**
131
     /**
126
      * The jwt token to pass to the search service.
132
      * The jwt token to pass to the search service.
127
      */
133
      */
149
         dialOutEnabled,
155
         dialOutEnabled,
150
         peopleSearchQueryTypes,
156
         peopleSearchQueryTypes,
151
         peopleSearchUrl,
157
         peopleSearchUrl,
158
+        sipInviteEnabled,
152
         jwt
159
         jwt
153
     } = options;
160
     } = options;
154
 
161
 
233
                 });
240
                 });
234
             }
241
             }
235
 
242
 
243
+            if (sipInviteEnabled && isASipAddress(text)) {
244
+                results.push({
245
+                    type: 'sip',
246
+                    address: text
247
+                });
248
+            }
249
+
236
             return results;
250
             return results;
237
         });
251
         });
238
 }
252
 }
374
         && conference && conference.isSIPCallingSupported();
388
         && conference && conference.isSIPCallingSupported();
375
 }
389
 }
376
 
390
 
391
+/**
392
+ * Determines if inviting sip endpoints is enabled or not.
393
+ *
394
+ * @param {Object} state - Current state.
395
+ * @returns {boolean} Indication of whether dial out is currently enabled.
396
+ */
397
+export function isSipInviteEnabled(state: Object): boolean {
398
+    const { sipInviteUrl } = state['features/base/config'];
399
+    const { features = {} } = getLocalParticipant(state);
400
+
401
+    return state['features/base/jwt'].jwt
402
+        && Boolean(sipInviteUrl)
403
+        && String(features['sip-outbound-call']) === 'true';
404
+}
405
+
377
 /**
406
 /**
378
  * Checks whether a string looks like it could be for a phone number.
407
  * Checks whether a string looks like it could be for a phone number.
379
  *
408
  *
392
     return Boolean(digits.length);
421
     return Boolean(digits.length);
393
 }
422
 }
394
 
423
 
424
+/**
425
+ * Checks whether a string matches a sip address format.
426
+ *
427
+ * @param {string} text - The text to check.
428
+ * @returns {boolean} True if provided text matches a sip address format.
429
+ */
430
+function isASipAddress(text: string): boolean {
431
+    return SIP_ADDRESS_REGEX.test(text);
432
+}
433
+
395
 /**
434
 /**
396
  * RegExp to use to determine if some text might be a phone number.
435
  * RegExp to use to determine if some text might be a phone number.
397
  *
436
  *
764
         || typeof interfaceConfig.SHARING_FEATURES === 'undefined'
803
         || typeof interfaceConfig.SHARING_FEATURES === 'undefined'
765
         || (interfaceConfig.SHARING_FEATURES.length && interfaceConfig.SHARING_FEATURES.indexOf(sharingFeature) > -1);
804
         || (interfaceConfig.SHARING_FEATURES.length && interfaceConfig.SHARING_FEATURES.indexOf(sharingFeature) > -1);
766
 }
805
 }
806
+
807
+/**
808
+ * Sends a post request to an invite service.
809
+ *
810
+ * @param {Array} inviteItems - The list of the "sip" type items to invite.
811
+ * @param {string} sipInviteUrl - The invite service that generates the invitation.
812
+ * @param {string} jwt - The jwt token.
813
+ * @param {string} roomName - The name to the conference.
814
+ * @param {string} displayName - The user display name.
815
+ * @returns {Promise} - The promise created by the request.
816
+ */
817
+export function inviteSipEndpoints( // eslint-disable-line max-params
818
+        inviteItems: Array<Object>,
819
+        sipInviteUrl: string,
820
+        jwt: string,
821
+        roomName: string,
822
+        displayName: string
823
+): Promise<void> {
824
+    if (inviteItems.length === 0) {
825
+        return Promise.resolve();
826
+    }
827
+
828
+    return fetch(
829
+       `${sipInviteUrl}?token=${jwt}`,
830
+       {
831
+           body: JSON.stringify({
832
+               callParams: {
833
+                   callUrlInfo: {
834
+                       baseUrl: window.location.origin,
835
+                       callName: roomName
836
+                   }
837
+               },
838
+               sipClientParams: {
839
+                   displayName,
840
+                   sipAddress: inviteItems.map(item => item.address)
841
+               }
842
+           }),
843
+           method: 'POST',
844
+           headers: {
845
+               'Content-Type': 'application/json'
846
+           }
847
+       }
848
+    );
849
+}

Loading…
Откажи
Сачувај