Bläddra i källkod

Adds new format of phoneList service and re-design dial in numbers page. (#3903)

* Adds new format of phoneList service and re-design dial in numbers page.

Adds flags and country names (with translations) for the numbers if using the new format.

* Fixes tests and fixes get default number.

* Updates swagger with new format.

* Moves html back yo table.

Fixes displaying on mobile and also the tel: URI generation. The tel: URI is tested on Android and iOS and seems to work (Android was not interpreting 'p', but both seems to like ',').

* Fixes a wrong return statement.

* Small fixes.
master
Дамян Минков 6 år sedan
förälder
incheckning
ea4d49f2a0
Inget konto är kopplat till bidragsgivarens mejladress

+ 22
- 2
css/deep-linking/_mobile.scss Visa fil

28
         max-width: 40em;
28
         max-width: 40em;
29
         padding: 35px 0 40px 0;
29
         padding: 35px 0 40px 0;
30
         text-align: center;
30
         text-align: center;
31
-        width: 75%;
31
+        width: 90%;
32
 
32
 
33
         a:active {
33
         a:active {
34
             text-decoration: none;
34
             text-decoration: none;
46
 
46
 
47
     &__text,
47
     &__text,
48
     .deep-linking-dial-in  {
48
     .deep-linking-dial-in  {
49
-        font-size: 1.2em;
49
+        font-size: 1em;
50
         line-height: em(29px, 21px);
50
         line-height: em(29px, 21px);
51
         margin-bottom: 0.65em;
51
         margin-bottom: 0.65em;
52
 
52
 
59
                 font-size: em(21, 18);
59
                 font-size: em(21, 18);
60
             }
60
             }
61
         }
61
         }
62
+
63
+        table {
64
+            font-size: 1em;
65
+        }
66
+
67
+        .dial-in-conference-id {
68
+            margin: 10px 0 10px 0;
69
+        }
70
+
71
+        .dial-in-conference-description {
72
+            font-size: 0.8em;
73
+        }
74
+
75
+        .toll-free-list {
76
+            min-width: 80px;
77
+        }
78
+
79
+        .numbers-list {
80
+            min-width: 150px;
81
+        }
62
     }
82
     }
63
 
83
 
64
     &__href {
84
     &__href {

+ 54
- 8
css/modals/invite/_info.scss Visa fil

124
     }
124
     }
125
 }
125
 }
126
 
126
 
127
+.dial-in-numbers-list {
128
+    margin-top: 20px;
129
+    font-size: 12px;
130
+    line-height: 24px;
131
+    border-collapse: separate;
132
+    border-spacing: 0 5px;
133
+
134
+    thead {
135
+        text-align: left;
136
+    }
137
+
138
+    td,
139
+    th {
140
+        border-bottom: 1px solid #d1dbe8;
141
+    }
142
+
143
+    .flag {
144
+        border-bottom-style: none;
145
+        width: 30px;
146
+        vertical-align: top;
147
+    }
148
+
149
+    .country {
150
+        font-weight: bold;
151
+        vertical-align: top;
152
+        padding: 0 20px 0 0;
153
+    }
154
+
155
+    ul {
156
+        padding: 0px 0px 0px 0px;
157
+    }
158
+
159
+    .numbers-list {
160
+        list-style: none;
161
+        padding: 0 20px 0 0;
162
+    }
163
+
164
+    .toll-free-list {
165
+        font-weight: bold;
166
+        list-style: none;
167
+        vertical-align: top;
168
+    }
169
+}
170
+
127
 .dial-in-page {
171
 .dial-in-page {
128
     align-items: center;
172
     align-items: center;
129
     box-sizing: border-box;
173
     box-sizing: border-box;
130
     display: flex;
174
     display: flex;
131
     flex-direction: column;
175
     flex-direction: column;
132
-    font-size: 24px;
176
+    font-size: 12px;
133
     max-height: 100%;
177
     max-height: 100%;
134
     overflow: auto;
178
     overflow: auto;
135
-    padding: 25px;
136
     position: absolute;
179
     position: absolute;
137
     transform: translateY(-50%);
180
     transform: translateY(-50%);
138
     top: 50%;
181
     top: 50%;
139
     width: 100%;
182
     width: 100%;
140
 
183
 
141
-    .dial-in-numbers-list {
142
-        font-size: 24px;
143
-        margin-top: 20px;
144
-    }
145
-
146
     .dial-in-conference-id {
184
     .dial-in-conference-id {
147
         text-align: center;
185
         text-align: center;
148
         min-width: 200px;
186
         min-width: 200px;
149
-        width: 30%;
187
+    }
188
+
189
+    .dial-in-conference-name,
190
+    .dial-in-conference-pin {
191
+        font-size: 18px;
192
+    }
193
+
194
+    .dial-in-conference-description {
195
+        margin: 12px;
150
     }
196
     }
151
 }
197
 }
152
 
198
 

+ 11
- 1
debian/rules Visa fil

3
 # Uncomment this to turn on verbose mode.
3
 # Uncomment this to turn on verbose mode.
4
 #export DH_VERBOSE=1
4
 #export DH_VERBOSE=1
5
 
5
 
6
+LANGUAGES := $(shell node -p "Object.keys(require('./lang/languages.json')).join(' ')")
7
+COUNTRIES_DIR := node_modules/i18n-iso-countries/langs
8
+
6
 %:
9
 %:
7
 	dh $@
10
 	dh $@
8
 
11
 
9
 # we skip making Makefile exists for updating browserify modules when developing
12
 # we skip making Makefile exists for updating browserify modules when developing
10
 override_dh_auto_build:
13
 override_dh_auto_build:
11
 
14
 
12
-override_dh_install:
15
+override_dh_install: $(LANGUAGES)
13
 	dh_installdirs
16
 	dh_installdirs
14
 	dh_install -X/config.js -X/package.json
17
 	dh_install -X/config.js -X/package.json
18
+
19
+$(LANGUAGES):
20
+	if [ -f $(COUNTRIES_DIR)/$@.json ] ; \
21
+	then \
22
+		dh_install -pjitsi-meet-web $(COUNTRIES_DIR)/$@.json usr/share/jitsi-meet/lang/; \
23
+		mv debian/jitsi-meet-web/usr/share/jitsi-meet/lang/$@.json debian/jitsi-meet-web/usr/share/jitsi-meet/lang/countries-$@.json; \
24
+	fi;

+ 21
- 17
doc/cloud-api.swagger Visa fil

71
             $ref: "#/definitions/ConferenceMapperDetails"
71
             $ref: "#/definitions/ConferenceMapperDetails"
72
         405:
72
         405:
73
           description: "Invalid input"
73
           description: "Invalid input"
74
- 
74
+
75
   /phoneNumberList:
75
   /phoneNumberList:
76
     get:
76
     get:
77
       tags:
77
       tags:
96
     name: "Authorization"
96
     name: "Authorization"
97
     in: "header"
97
     in: "header"
98
 definitions:
98
 definitions:
99
- 
99
+
100
   ConferenceMapperRequest:
100
   ConferenceMapperRequest:
101
     description: "Request to create or find a conference mapping"
101
     description: "Request to create or find a conference mapping"
102
     type: "object"
102
     type: "object"
114
       domain:
114
       domain:
115
         type: "string"
115
         type: "string"
116
         description: "Domain part of the conference.  Used if 'conference' is not provided.  Defaults to domain of the API endpoint.   Used to generate a 'conference' value (search by conference)"
116
         description: "Domain part of the conference.  Used if 'conference' is not provided.  Defaults to domain of the API endpoint.   Used to generate a 'conference' value (search by conference)"
117
- 
117
+
118
   ConferenceMapperDetails:
118
   ConferenceMapperDetails:
119
     description: "Conference mapping between conference JID and numeric ID"
119
     description: "Conference mapping between conference JID and numeric ID"
120
     type: "object"
120
     type: "object"
126
         type: "string"
126
         type: "string"
127
         format: "JID"
127
         format: "JID"
128
         description: "Full JID for the conference OR boolean false if no conference was found (search by ID)"
128
         description: "Full JID for the conference OR boolean false if no conference was found (search by ID)"
129
- 
129
+
130
   PhoneNumberList:
130
   PhoneNumberList:
131
-    type: "object"
132
-    properties:
133
-      numbersEnabled:
134
-        type: "boolean"
135
-        description: "Control flag for Jitsi Meet user interface.  Must be set to true for Jitsi Meet to display phone-in UI elements"
136
-      numbers:
131
+    description: "List of dial in numbers for the conference."
132
+    type: "array"
133
+    items:
137
         type: "object"
134
         type: "object"
138
-        description: "Keys are Country Names, each value is an array of phone numbers"
139
-        additionalProperties:
140
-          type: "array"
141
-          items:
135
+        properties:
136
+          countryCode:
137
+            type: "string"
138
+            description: "ISO 3166-1 country code. Alpha-2 supported."
139
+          default:
140
+            type: "boolean"
141
+            description: "Whether this number is the default one to show. Optional."
142
+          formattedNumber:
142
             type: "string"
143
             type: "string"
143
-            format: "phone"
144
- 
144
+            description: "The formatted telephone number to show."
145
+          tollFree:
146
+            type: "boolean"
147
+            description: "Whether the number is toll free number."
148
+
145
 externalDocs:
149
 externalDocs:
146
   description: "Find out more about the Jitsi Cloud API"
150
   description: "Find out more about the Jitsi Cloud API"
147
-  url: "https://jitsi.org/CloudAPI"
151
+  url: "https://jitsi.org/CloudAPI"

+ 3
- 2
lang/main.json Visa fil

344
         "cancelPassword": "Cancel password",
344
         "cancelPassword": "Cancel password",
345
         "conferenceURL": "Link:",
345
         "conferenceURL": "Link:",
346
         "country": "Country",
346
         "country": "Country",
347
-        "dialANumber": "To join your meeting, dial one of these numbers and then enter this PIN: __conferenceID__#",
347
+        "dialANumber": "To join your meeting, dial one of these numbers and then enter the pin.",
348
         "dialInConferenceID": "PIN:",
348
         "dialInConferenceID": "PIN:",
349
-        "dialInNotSupported": "Sorry, dialing in is currently not suppported.",
349
+        "dialInNotSupported": "Sorry, dialing in is currently not supported.",
350
         "dialInNumber": "Dial-in:",
350
         "dialInNumber": "Dial-in:",
351
+        "dialInTollFree": "Toll Free",
351
         "genericError": "Whoops, something went wrong.",
352
         "genericError": "Whoops, something went wrong.",
352
         "inviteLiveStream": "To view the live stream of this meeting, click this link: __url__",
353
         "inviteLiveStream": "To view the live stream of this meeting, click this link: __url__",
353
         "invitePhone": "To join by phone, dial __number__ and enter this PIN: __conferenceID__#",
354
         "invitePhone": "To join by phone, dial __number__ and enter this PIN: __conferenceID__#",

+ 5
- 0
package-lock.json Visa fil

7494
       "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
7494
       "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
7495
       "dev": true
7495
       "dev": true
7496
     },
7496
     },
7497
+    "i18n-iso-countries": {
7498
+      "version": "3.7.8",
7499
+      "resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-3.7.8.tgz",
7500
+      "integrity": "sha512-NkT3lRiw7D4kKtSAVjVdHCvGlc2UOe0ALKa9IfEx0LkEDf0q3YgjP/veVk0d/OZ7yqUNzV8aJP4lJc6RPj++Gw=="
7501
+    },
7497
     "i18next": {
7502
     "i18next": {
7498
       "version": "8.4.3",
7503
       "version": "8.4.3",
7499
       "resolved": "https://registry.npmjs.org/i18next/-/i18next-8.4.3.tgz",
7504
       "resolved": "https://registry.npmjs.org/i18next/-/i18next-8.4.3.tgz",

+ 1
- 0
package.json Visa fil

37
     "@webcomponents/url": "0.7.1",
37
     "@webcomponents/url": "0.7.1",
38
     "amplitude-js": "4.5.2",
38
     "amplitude-js": "4.5.2",
39
     "dropbox": "4.0.9",
39
     "dropbox": "4.0.9",
40
+    "i18n-iso-countries": "3.7.8",
40
     "i18next": "8.4.3",
41
     "i18next": "8.4.3",
41
     "i18next-browser-languagedetector": "2.0.0",
42
     "i18next-browser-languagedetector": "2.0.0",
42
     "i18next-xhr-backend": "1.4.2",
43
     "i18next-xhr-backend": "1.4.2",

+ 1
- 1
react/features/base/i18n/functions.js Visa fil

13
 export function translate(component, options = { wait: true }) {
13
 export function translate(component, options = { wait: true }) {
14
     // Use the default list of namespaces.
14
     // Use the default list of namespaces.
15
     return (
15
     return (
16
-        reactI18nextTranslate([ 'main', 'languages' ], options)(
16
+        reactI18nextTranslate([ 'main', 'languages', 'countries' ], options)(
17
             component));
17
             component));
18
 }
18
 }
19
 
19
 

+ 8
- 1
react/features/base/i18n/i18next.js Visa fil

3
 import i18next from 'i18next';
3
 import i18next from 'i18next';
4
 import I18nextXHRBackend from 'i18next-xhr-backend';
4
 import I18nextXHRBackend from 'i18next-xhr-backend';
5
 
5
 
6
+import COUNTRIES_RESOURCES from 'i18n-iso-countries/langs/en.json';
6
 import LANGUAGES_RESOURCES from '../../../../lang/languages.json';
7
 import LANGUAGES_RESOURCES from '../../../../lang/languages.json';
7
 import MAIN_RESOURCES from '../../../../lang/main.json';
8
 import MAIN_RESOURCES from '../../../../lang/main.json';
8
 
9
 
51
     load: 'unspecific',
52
     load: 'unspecific',
52
     ns: {
53
     ns: {
53
         defaultNs: 'main',
54
         defaultNs: 'main',
54
-        namespaces: [ 'main', 'languages' ]
55
+        namespaces: [ 'main', 'languages', 'countries' ]
55
     },
56
     },
56
     resGetPath: 'lang/__ns__-__lng__.json',
57
     resGetPath: 'lang/__ns__-__lng__.json',
57
     useDataAttrOptions: true
58
     useDataAttrOptions: true
68
     .init(options);
69
     .init(options);
69
 
70
 
70
 // Add default language which is preloaded from the source code.
71
 // Add default language which is preloaded from the source code.
72
+i18next.addResourceBundle(
73
+    DEFAULT_LANGUAGE,
74
+    'countries',
75
+    COUNTRIES_RESOURCES,
76
+    /* deep */ true,
77
+    /* overwrite */ true);
71
 i18next.addResourceBundle(
78
 i18next.addResourceBundle(
72
     DEFAULT_LANGUAGE,
79
     DEFAULT_LANGUAGE,
73
     'languages',
80
     'languages',

+ 15
- 2
react/features/invite/components/dial-in-summary/ConferenceID.web.js Visa fil

14
      */
14
      */
15
     conferenceID: number,
15
     conferenceID: number,
16
 
16
 
17
+    /**
18
+     * The name of the conference.
19
+     */
20
+    conferenceName: ?string,
21
+
17
     /**
22
     /**
18
      * Invoked to obtain translated strings.
23
      * Invoked to obtain translated strings.
19
      */
24
      */
33
      * @returns {ReactElement}
38
      * @returns {ReactElement}
34
      */
39
      */
35
     render() {
40
     render() {
36
-        const { conferenceID, t } = this.props;
41
+        const { conferenceID, conferenceName, t } = this.props;
37
 
42
 
38
         return (
43
         return (
39
             <div className = 'dial-in-conference-id'>
44
             <div className = 'dial-in-conference-id'>
40
-                { t('info.dialANumber', { conferenceID }) }
45
+                <div className = 'dial-in-conference-name'>
46
+                    { conferenceName }
47
+                </div>
48
+                <div className = 'dial-in-conference-description'>
49
+                    { t('info.dialANumber') }
50
+                </div>
51
+                <div className = 'dial-in-conference-pin'>
52
+                    { `${t('info.dialInConferenceID')} ${conferenceID}` }
53
+                </div>
41
             </div>
54
             </div>
42
         );
55
         );
43
     }
56
     }

+ 14
- 8
react/features/invite/components/dial-in-summary/DialInSummary.web.js Visa fil

56
     loading: boolean,
56
     loading: boolean,
57
 
57
 
58
     /**
58
     /**
59
-     * The dial-in numbers. entered by the local participant.
59
+     * The dial-in numbers to be displayed.
60
      */
60
      */
61
-    numbers: ?Array<Object>,
61
+    numbers: ?Array<Object> | ?Object,
62
 
62
 
63
     /**
63
     /**
64
      * Whether or not dial-in is allowed.
64
      * Whether or not dial-in is allowed.
143
                 conferenceID
143
                 conferenceID
144
                     ? <ConferenceID
144
                     ? <ConferenceID
145
                         conferenceID = { conferenceID }
145
                         conferenceID = { conferenceID }
146
+                        conferenceName = { this.props.room }
146
                         key = 'conferenceID' />
147
                         key = 'conferenceID' />
147
                     : null,
148
                     : null,
148
                 <NumbersList
149
                 <NumbersList
238
      * Callback invoked when fetching dial-in numbers succeeds. Sets the
239
      * Callback invoked when fetching dial-in numbers succeeds. Sets the
239
      * internal to show the numbers.
240
      * internal to show the numbers.
240
      *
241
      *
241
-     * @param {Object} response - The response from fetching dial-in numbers.
242
+     * @param {Array|Object} response - The response from fetching
243
+     * dial-in numbers.
242
      * @param {Array|Object} response.numbers - The dial-in numbers.
244
      * @param {Array|Object} response.numbers - The dial-in numbers.
243
-     * @param {boolean} reponse.numbersEnabled - Whether or not dial-in is
244
-     * enabled.
245
+     * @param {boolean} response.numbersEnabled - Whether or not dial-in is
246
+     * enabled, old syntax that is deprecated.
245
      * @private
247
      * @private
246
      * @returns {void}
248
      * @returns {void}
247
      */
249
      */
248
-    _onGetNumbersSuccess({ numbers, numbersEnabled }) {
250
+    _onGetNumbersSuccess(
251
+            response: Array<Object> | { numbersEnabled?: boolean }) {
252
+
249
         this.setState({
253
         this.setState({
250
-            numbersEnabled,
251
-            numbers
254
+            numbersEnabled:
255
+                Array.isArray(response)
256
+                    ? response.length > 0 : response.numbersEnabled,
257
+            numbers: response
252
         });
258
         });
253
     }
259
     }
254
 
260
 

+ 128
- 52
react/features/invite/components/dial-in-summary/NumbersList.web.js Visa fil

17
     conferenceID: number,
17
     conferenceID: number,
18
 
18
 
19
     /**
19
     /**
20
-     * The phone numbers to display. Can be an array of numbers or an object
21
-     * with countries as keys and an array of numbers as values.
20
+     * The phone numbers to display. Can be an array of number Objects or an
21
+     * object with countries as keys and an array of numbers as values.
22
      */
22
      */
23
-    numbers: { [string]: Array<string> } | Array<string>,
23
+    numbers: { [string]: Array<string> } | Array<Object>,
24
 
24
 
25
     /**
25
     /**
26
      * Invoked to obtain translated strings.
26
      * Invoked to obtain translated strings.
41
      * @returns {ReactElement}
41
      * @returns {ReactElement}
42
      */
42
      */
43
     render() {
43
     render() {
44
-        const { numbers, t } = this.props;
44
+        const { numbers } = this.props;
45
+
46
+        return this._renderWithCountries(numbers);
47
+    }
48
+
49
+    /**
50
+     * Renders rows of countries and associated phone numbers.
51
+     *
52
+     * @param {Object|Array<Object>} numbersMapping - An object with country
53
+     * names as keys and values as arrays of phone numbers.
54
+     * @private
55
+     * @returns {ReactElement[]}
56
+     */
57
+    _renderWithCountries(
58
+            numbersMapping: { numbers: Array<string> } | Array<Object>) {
59
+        const { t } = this.props;
60
+        let hasFlags = false, numbers;
61
+
62
+        if (Array.isArray(numbersMapping)) {
63
+            hasFlags = true;
64
+            numbers = numbersMapping.reduce(
65
+                (resultNumbers, number) => {
66
+                    const countryName
67
+                        = t(`countries:countries.${number.countryCode}`);
68
+
69
+                    if (resultNumbers[countryName]) {
70
+                        resultNumbers[countryName].push(number);
71
+                    } else {
72
+                        resultNumbers[countryName] = [ number ];
73
+                    }
74
+
75
+                    return resultNumbers;
76
+                }, {});
77
+        } else {
78
+            numbers = {};
79
+
80
+            for (const [ country, numbersArray ]
81
+                of Object.entries(numbersMapping.numbers)) {
82
+
83
+                if (Array.isArray(numbersArray)) {
84
+                    /* eslint-disable arrow-body-style */
85
+                    const formattedNumbers = numbersArray.map(number => ({
86
+                        formattedNumber: number
87
+                    }));
88
+                    /* eslint-enable arrow-body-style */
89
+
90
+                    numbers[country] = formattedNumbers;
91
+                }
92
+            }
93
+        }
94
+
95
+        const rows = [];
96
+
97
+        Object.keys(numbers).forEach((countryName: string) => {
98
+            const numbersArray = numbers[countryName];
99
+
100
+            rows.push(
101
+                <tr
102
+                    className = 'number-group'
103
+                    key = { countryName }>
104
+                    { this._renderFlag(numbersArray[0].countryCode) }
105
+                    <td className = 'country' >{ countryName }</td>
106
+                    <td className = 'numbers-list-column'>
107
+                        { this._renderNumbersList(numbersArray) }
108
+                    </td>
109
+                    <td className = 'toll-free-list-column' >
110
+                        { this._renderNumbersTollFreeList(numbersArray) }
111
+                    </td>
112
+                </tr>
113
+            );
114
+        });
45
 
115
 
46
         return (
116
         return (
47
             <table className = 'dial-in-numbers-list'>
117
             <table className = 'dial-in-numbers-list'>
48
                 <thead>
118
                 <thead>
49
                     <tr>
119
                     <tr>
50
-                        { Array.isArray(numbers)
51
-                            ? null
52
-                            : <th>{ t('info.country') }</th> }
120
+                        { hasFlags ? <th /> : null}
121
+                        <th>{ t('info.country') }</th>
53
                         <th>{ t('info.numbers') }</th>
122
                         <th>{ t('info.numbers') }</th>
123
+                        <th />
54
                     </tr>
124
                     </tr>
55
                 </thead>
125
                 </thead>
56
                 <tbody className = 'dial-in-numbers-body'>
126
                 <tbody className = 'dial-in-numbers-body'>
57
-                    { Array.isArray(numbers)
58
-                        ? numbers.map(this._renderNumberRow)
59
-                        : this._renderWithCountries(numbers) }
127
+                    { rows }
60
                 </tbody>
128
                 </tbody>
61
-            </table>);
129
+            </table>
130
+        );
62
     }
131
     }
63
 
132
 
64
     /**
133
     /**
65
-     * Renders rows of countries and associated phone numbers.
134
+     * Renders a div container for a phone number.
66
      *
135
      *
67
-     * @param {Object} numbersMapping - An object with country names as keys
68
-     * and values as arrays of phone numbers.
136
+     * @param {string} countryCode - The phone number to display.
69
      * @private
137
      * @private
70
-     * @returns {ReactElement[]}
138
+     * @returns {ReactElement}
71
      */
139
      */
72
-    _renderWithCountries(numbersMapping: Object) {
73
-        const rows = [];
140
+    _renderFlag(countryCode) {
141
+        const OFFSET = 127397;
74
 
142
 
75
-        for (const [ country, numbers ] of Object.entries(numbersMapping)) {
76
-            if (!Array.isArray(numbers)) {
77
-                return;
78
-            }
79
-
80
-            const formattedNumbers = numbers.map(number => {
81
-                if (typeof number === 'string') {
82
-                    return this._renderNumberDiv(number);
83
-                }
143
+        if (countryCode) {
144
+            // ensure country code is all caps
145
+            const cc = countryCode.toUpperCase();
84
 
146
 
85
-                return null;
86
-            });
147
+            // return the emoji flag corresponding to country_code or null
148
+            const countryFlag = /^[A-Z]{2}$/.test(cc)
149
+                ? String.fromCodePoint(...[ ...cc ]
150
+                    .map(c => c.charCodeAt() + OFFSET))
151
+                : null;
87
 
152
 
88
-            rows.push(
89
-                <tr key = { country }>
90
-                    <td>{ country }</td>
91
-                    <td className = 'dial-in-numbers'>{ formattedNumbers }</td>
92
-                </tr>
93
-            );
153
+            return <td className = 'flag'>{ countryFlag }</td>;
94
         }
154
         }
95
 
155
 
96
-        return rows;
156
+        return null;
97
     }
157
     }
98
 
158
 
99
     /**
159
     /**
100
-     * Renders a table row for a phone number.
160
+     * Renders a div container for a phone number.
101
      *
161
      *
102
-     * @param {string} number - The phone number to display.
162
+     * @param {Array} numbers - The phone number to display.
103
      * @private
163
      * @private
104
      * @returns {ReactElement[]}
164
      * @returns {ReactElement[]}
105
      */
165
      */
106
-    _renderNumberRow(number) {
166
+    _renderNumbersList(numbers) {
167
+        const numbersListItems = numbers.map(number =>
168
+            (<li
169
+                className = 'dial-in-number'
170
+                key = { number.formattedNumber }>
171
+                { this._renderNumberLink(number.formattedNumber) }
172
+            </li>));
173
+
107
         return (
174
         return (
108
-            <tr key = { number }>
109
-                <td className = 'dial-in-number'>
110
-                    { this._renderNumberLink(number) }
111
-                </td>
112
-            </tr>
175
+            <ul className = 'numbers-list'>
176
+                { numbersListItems }
177
+            </ul>
113
         );
178
         );
114
     }
179
     }
115
 
180
 
116
     /**
181
     /**
117
-     * Renders a div container for a phone number.
182
+     * Renders list with a toll free text on the position where there is a
183
+     * number marked as toll free.
118
      *
184
      *
119
-     * @param {string} number - The phone number to display.
185
+     * @param {Array} numbers - The phone number that are displayed.
120
      * @private
186
      * @private
121
      * @returns {ReactElement[]}
187
      * @returns {ReactElement[]}
122
      */
188
      */
123
-    _renderNumberDiv(number) {
189
+    _renderNumbersTollFreeList(numbers) {
190
+        const { t } = this.props;
191
+
192
+        const tollNumbersListItems = numbers.map(number =>
193
+            (<li
194
+                className = 'toll-free'
195
+                key = { number.formattedNumber }>
196
+                { number.tollFree ? t('info.dialInTollFree') : '' }
197
+            </li>));
198
+
124
         return (
199
         return (
125
-            <div
126
-                className = 'dial-in-number'
127
-                key = { number }>
128
-                { this._renderNumberLink(number) }
129
-            </div>
200
+            <ul className = 'toll-free-list'>
201
+                { tollNumbersListItems }
202
+            </ul>
130
         );
203
         );
131
     }
204
     }
132
 
205
 
141
      */
214
      */
142
     _renderNumberLink(number) {
215
     _renderNumberLink(number) {
143
         if (this.props.clickableNumbers) {
216
         if (this.props.clickableNumbers) {
217
+            // Url encode # to %23, Android phone was cutting the # after
218
+            // clicking it.
219
+            // Seems that using ',' and '%23' works on iOS and Android.
144
             return (
220
             return (
145
                 <a
221
                 <a
146
-                    href = { `tel:${number}p${this.props.conferenceID}#` }
222
+                    href = { `tel:${number},${this.props.conferenceID}%23` }
147
                     key = { number } >
223
                     key = { number } >
148
                     { number }
224
                     { number }
149
                 </a>
225
                 </a>

+ 3
- 7
react/features/invite/components/info-dialog/InfoDialog.web.js Visa fil

121
         let phoneNumber = state.phoneNumber;
121
         let phoneNumber = state.phoneNumber;
122
 
122
 
123
         if (!state.phoneNumber && props.dialIn.numbers) {
123
         if (!state.phoneNumber && props.dialIn.numbers) {
124
-            const { defaultCountry, numbers } = props.dialIn;
125
-
126
-            phoneNumber = _getDefaultPhoneNumber(numbers, defaultCountry);
124
+            phoneNumber = _getDefaultPhoneNumber(props.dialIn);
127
         }
125
         }
128
 
126
 
129
         return {
127
         return {
157
     constructor(props: Props) {
155
     constructor(props: Props) {
158
         super(props);
156
         super(props);
159
 
157
 
160
-        const { defaultCountry, numbers } = props.dialIn;
161
-
162
-        if (numbers) {
158
+        if (props.dialIn && props.dialIn.numbers) {
163
             this.state.phoneNumber
159
             this.state.phoneNumber
164
-                = _getDefaultPhoneNumber(numbers, defaultCountry);
160
+                = _getDefaultPhoneNumber(props.dialIn.numbers);
165
         }
161
         }
166
 
162
 
167
         /**
163
         /**

+ 37
- 18
react/features/invite/functions.js Visa fil

54
 /**
54
 /**
55
  * Sends a GET request for phone numbers used to dial into a conference.
55
  * Sends a GET request for phone numbers used to dial into a conference.
56
  *
56
  *
57
- * @param {string} url - The service that returns confernce dial-in numbers.
57
+ * @param {string} url - The service that returns conference dial-in numbers.
58
  * @param {string} roomName - The conference name to find the associated
58
  * @param {string} roomName - The conference name to find the associated
59
  * conference ID.
59
  * conference ID.
60
  * @param {string} mucURL - In which MUC the conference exists.
60
  * @param {string} mucURL - In which MUC the conference exists.
61
  * @returns {Promise} - The promise created by the request. The returned numbers
61
  * @returns {Promise} - The promise created by the request. The returned numbers
62
- * may be an array of numbers or an object with countries as keys and arrays of
63
- * phone number strings.
62
+ * may be an array of Objects containing numbers, with keys countryCode,
63
+ * tollFree, formattedNumber or an object with countries as keys and arrays of
64
+ * phone number strings, as the second one should not be used and is deprecated.
64
  */
65
  */
65
 export function getDialInNumbers(
66
 export function getDialInNumbers(
66
         url: string,
67
         url: string,
432
             numbersPromise = Promise.all([
433
             numbersPromise = Promise.all([
433
                 getDialInNumbers(dialInNumbersUrl, room, mucURL),
434
                 getDialInNumbers(dialInNumbersUrl, room, mucURL),
434
                 getDialInConferenceID(dialInConfCodeUrl, room, mucURL)
435
                 getDialInConferenceID(dialInConfCodeUrl, room, mucURL)
435
-            ]).then(([ { defaultCountry, numbers }, {
436
+            ]).then(([ numbers, {
436
                 conference, id, message } ]) => {
437
                 conference, id, message } ]) => {
437
 
438
 
438
                 if (!conference || !id) {
439
                 if (!conference || !id) {
440
                 }
441
                 }
441
 
442
 
442
                 return {
443
                 return {
443
-                    defaultCountry,
444
                     numbers,
444
                     numbers,
445
                     conferenceID: id
445
                     conferenceID: id
446
                 };
446
                 };
448
         }
448
         }
449
 
449
 
450
         return numbersPromise.then(
450
         return numbersPromise.then(
451
-            ({ conferenceID, defaultCountry, numbers }) => {
452
-                const phoneNumber
453
-                    = _getDefaultPhoneNumber(numbers, defaultCountry) || '';
451
+            ({ conferenceID, numbers }) => {
452
+                const phoneNumber = _getDefaultPhoneNumber(numbers) || '';
454
 
453
 
455
                 return `${
454
                 return `${
456
                     i18next.t('info.dialInNumber')} ${
455
                     i18next.t('info.dialInNumber')} ${
513
  *
512
  *
514
  * @param {Array<string>|Object} dialInNumbers - The array or object of
513
  * @param {Array<string>|Object} dialInNumbers - The array or object of
515
  * numbers to choose a number from.
514
  * numbers to choose a number from.
516
- * @param {string} defaultCountry - The country code for the country
517
- * whose phone number should display.
518
  * @private
515
  * @private
519
  * @returns {string|null}
516
  * @returns {string|null}
520
  */
517
  */
521
 export function _getDefaultPhoneNumber(
518
 export function _getDefaultPhoneNumber(
522
-        dialInNumbers: Object,
523
-        defaultCountry: string = 'US'): ?string {
519
+        dialInNumbers: Object): ?string {
520
+    const defValueForDefaultCountry = 'US';
521
+
524
     if (Array.isArray(dialInNumbers)) {
522
     if (Array.isArray(dialInNumbers)) {
525
-        // Dumbly return the first number if an array.
526
-        return dialInNumbers[0];
527
-    } else if (Object.keys(dialInNumbers).length > 0) {
528
-        const defaultNumbers = dialInNumbers[defaultCountry];
523
+        // new syntax follows
524
+        // find the default country inside dialInNumbers, US one
525
+        // or return the first one
526
+        let defaultNumber = dialInNumbers.find(number => number.default);
527
+
528
+        if (!defaultNumber) {
529
+            defaultNumber = dialInNumbers.find(({ countryCode }) =>
530
+                countryCode === defValueForDefaultCountry);
531
+        }
532
+
533
+        if (defaultNumber) {
534
+            return defaultNumber.formattedNumber;
535
+        }
536
+
537
+        return dialInNumbers.length > 0
538
+            ? dialInNumbers[0].formattedNumber : null;
539
+    }
540
+
541
+    const {
542
+        defaultCountry = defValueForDefaultCountry,
543
+        numbers } = dialInNumbers;
544
+
545
+    if (numbers && Object.keys(numbers).length > 0) {
546
+        // deprecated and will be removed
547
+        const defaultNumbers = numbers[defaultCountry];
529
 
548
 
530
         if (defaultNumbers) {
549
         if (defaultNumbers) {
531
             return defaultNumbers[0];
550
             return defaultNumbers[0];
532
         }
551
         }
533
 
552
 
534
-        const firstRegion = Object.keys(dialInNumbers)[0];
553
+        const firstRegion = Object.keys(numbers)[0];
535
 
554
 
536
-        return firstRegion && firstRegion[0];
555
+        return firstRegion && numbers[firstRegion][0];
537
     }
556
     }
538
 
557
 
539
     return null;
558
     return null;

+ 16
- 7
react/features/invite/reducer.js Visa fil

10
     UPDATE_DIAL_IN_NUMBERS_SUCCESS
10
     UPDATE_DIAL_IN_NUMBERS_SUCCESS
11
 } from './actionTypes';
11
 } from './actionTypes';
12
 
12
 
13
+const logger = require('jitsi-meet-logger').getLogger(__filename);
14
+
13
 const DEFAULT_STATE = {
15
 const DEFAULT_STATE = {
14
     /**
16
     /**
15
      * The indicator which determines whether (the) {@code CalleeInfo} is
17
      * The indicator which determines whether (the) {@code CalleeInfo} is
54
         };
56
         };
55
 
57
 
56
     case UPDATE_DIAL_IN_NUMBERS_SUCCESS: {
58
     case UPDATE_DIAL_IN_NUMBERS_SUCCESS: {
57
-        const {
58
-            defaultCountry,
59
-            numbers,
60
-            numbersEnabled
61
-        } = action.dialInNumbers;
59
+        if (Array.isArray(action.dialInNumbers)) {
60
+            return {
61
+                ...state,
62
+                conferenceID: action.conferenceID,
63
+                numbers: action.dialInNumbers,
64
+                numbersEnabled: true
65
+            };
66
+        }
67
+
68
+        // this is the old format which is deprecated
69
+        logger.warn('Using deprecated API for retrieving phone numbers');
70
+
71
+        const { numbersEnabled } = action.dialInNumbers;
62
 
72
 
63
         return {
73
         return {
64
             ...state,
74
             ...state,
65
             conferenceID: action.conferenceID,
75
             conferenceID: action.conferenceID,
66
-            defaultCountry,
67
-            numbers,
76
+            numbers: action.dialInNumbers,
68
             numbersEnabled
77
             numbersEnabled
69
         };
78
         };
70
     }
79
     }

Laddar…
Avbryt
Spara