Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

NumbersList.js 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // @flow
  2. import React, { Component } from 'react';
  3. import { translate } from '../../../../base/i18n';
  4. import { Icon, IconSip } from '../../../../base/icons';
  5. type Props = {
  6. /**
  7. * Whether or not numbers should include links with the telephone protocol.
  8. */
  9. clickableNumbers: boolean,
  10. /**
  11. * The conference ID for dialing in.
  12. */
  13. conferenceID: number,
  14. /**
  15. * The phone numbers to display. Can be an array of number Objects or an
  16. * object with countries as keys and an array of numbers as values.
  17. */
  18. numbers: { [string]: Array<string> } | Array<Object>,
  19. /**
  20. * Invoked to obtain translated strings.
  21. */
  22. t: Function
  23. }
  24. /**
  25. * Displays a table with phone numbers to dial in to a conference.
  26. *
  27. * @augments Component
  28. */
  29. class NumbersList extends Component<Props> {
  30. /**
  31. * Implements React's {@link Component#render()}.
  32. *
  33. * @inheritdoc
  34. * @returns {ReactElement}
  35. */
  36. render() {
  37. const { numbers } = this.props;
  38. return this._renderWithCountries(numbers);
  39. }
  40. /**
  41. * Renders rows of countries and associated phone numbers.
  42. *
  43. * @param {Object|Array<Object>} numbersMapping - An object with country
  44. * names as keys and values as arrays of phone numbers.
  45. * @private
  46. * @returns {ReactElement[]}
  47. */
  48. _renderWithCountries(
  49. numbersMapping: { numbers: Array<string> } | Array<Object>) {
  50. const { t } = this.props;
  51. let hasFlags = false, numbers;
  52. if (Array.isArray(numbersMapping)) {
  53. hasFlags = true;
  54. numbers = numbersMapping.reduce(
  55. (resultNumbers, number) => {
  56. // The i18n-iso-countries package insists on upper case.
  57. const countryCode = number.countryCode.toUpperCase();
  58. let countryName;
  59. if (countryCode === 'SIP') {
  60. countryName = t('info.sip');
  61. } else {
  62. countryName = t(`countries:countries.${countryCode}`);
  63. // Some countries have multiple names as US ['United States of America', 'USA']
  64. // choose the first one if that is the case
  65. if (!countryName) {
  66. countryName = t(`countries:countries.${countryCode}.0`);
  67. }
  68. }
  69. if (resultNumbers[countryName]) {
  70. resultNumbers[countryName].push(number);
  71. } else {
  72. resultNumbers[countryName] = [ number ];
  73. }
  74. return resultNumbers;
  75. }, {});
  76. } else {
  77. numbers = {};
  78. for (const [ country, numbersArray ]
  79. of Object.entries(numbersMapping.numbers)) {
  80. if (Array.isArray(numbersArray)) {
  81. /* eslint-disable arrow-body-style */
  82. const formattedNumbers = numbersArray.map(number => ({
  83. formattedNumber: number
  84. }));
  85. /* eslint-enable arrow-body-style */
  86. numbers[country] = formattedNumbers;
  87. }
  88. }
  89. }
  90. const rows = [];
  91. Object.keys(numbers).forEach((countryName: string) => {
  92. const numbersArray = numbers[countryName];
  93. rows.push(
  94. <tr
  95. className = 'number-group'
  96. key = { countryName }>
  97. { this._renderFlag(numbersArray[0].countryCode) }
  98. <td className = 'country' >{ countryName }</td>
  99. <td className = 'numbers-list-column'>
  100. { this._renderNumbersList(numbersArray) }
  101. </td>
  102. <td className = 'toll-free-list-column' >
  103. { this._renderNumbersTollFreeList(numbersArray) }
  104. </td>
  105. </tr>
  106. );
  107. });
  108. return (
  109. <table className = 'dial-in-numbers-list'>
  110. <thead>
  111. <tr>
  112. { hasFlags ? <th /> : null}
  113. <th>{ t('info.country') }</th>
  114. <th>{ t('info.numbers') }</th>
  115. <th />
  116. </tr>
  117. </thead>
  118. <tbody className = 'dial-in-numbers-body'>
  119. { rows }
  120. </tbody>
  121. </table>
  122. );
  123. }
  124. /**
  125. * Renders a div container for a flag for the country of the phone number.
  126. *
  127. * @param {string} countryCode - The country code flag to display.
  128. * @private
  129. * @returns {ReactElement}
  130. */
  131. _renderFlag(countryCode) {
  132. if (countryCode) {
  133. return (
  134. <td className = 'flag-cell'>
  135. {countryCode === 'SIP'
  136. ? <Icon src = { IconSip } />
  137. : <i className = { `flag iti-flag ${countryCode}` } />
  138. }
  139. </td>);
  140. }
  141. return null;
  142. }
  143. /**
  144. * Renders a div container for a phone number.
  145. *
  146. * @param {Array} numbers - The phone number to display.
  147. * @private
  148. * @returns {ReactElement[]}
  149. */
  150. _renderNumbersList(numbers) {
  151. const numbersListItems = numbers.map(number =>
  152. (<li
  153. className = 'dial-in-number'
  154. key = { number.formattedNumber }>
  155. { this._renderNumberLink(number.formattedNumber) }
  156. </li>));
  157. return (
  158. <ul className = 'numbers-list'>
  159. { numbersListItems }
  160. </ul>
  161. );
  162. }
  163. /**
  164. * Renders list with a toll free text on the position where there is a
  165. * number marked as toll free.
  166. *
  167. * @param {Array} numbers - The phone number that are displayed.
  168. * @private
  169. * @returns {ReactElement[]}
  170. */
  171. _renderNumbersTollFreeList(numbers) {
  172. const { t } = this.props;
  173. const tollNumbersListItems = numbers.map(number =>
  174. (<li
  175. className = 'toll-free'
  176. key = { number.formattedNumber }>
  177. { number.tollFree ? t('info.dialInTollFree') : '' }
  178. </li>));
  179. return (
  180. <ul className = 'toll-free-list'>
  181. { tollNumbersListItems }
  182. </ul>
  183. );
  184. }
  185. /**
  186. * Renders a ReactElement for displaying a telephone number. If the
  187. * component prop {@code clickableNumbers} is true, then the number will
  188. * have a link with the telephone protocol.
  189. *
  190. * @param {string} number - The phone number to display.
  191. * @private
  192. * @returns {ReactElement}
  193. */
  194. _renderNumberLink(number) {
  195. if (this.props.clickableNumbers) {
  196. // Url encode # to %23, Android phone was cutting the # after
  197. // clicking it.
  198. // Seems that using ',' and '%23' works on iOS and Android.
  199. return (
  200. <a
  201. href = { `tel:${number},${this.props.conferenceID}%23` }
  202. key = { number } >
  203. { number }
  204. </a>
  205. );
  206. }
  207. return number;
  208. }
  209. }
  210. export default translate(NumbersList);