You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NumbersList.web.js 6.7KB

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