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.

PreMeetingScreen.tsx 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. import clsx from 'clsx';
  2. import React, { ReactNode, useEffect, useRef, useState } from 'react';
  3. import { connect } from 'react-redux';
  4. import { makeStyles } from 'tss-react/mui';
  5. import { IReduxState } from '../../../../app/types';
  6. import DeviceStatus from '../../../../prejoin/components/web/preview/DeviceStatus';
  7. import { isRoomNameEnabled } from '../../../../prejoin/functions.web';
  8. import Toolbox from '../../../../toolbox/components/web/Toolbox';
  9. import { isButtonEnabled } from '../../../../toolbox/functions.web';
  10. import { getConferenceName } from '../../../conference/functions';
  11. import { PREMEETING_BUTTONS, THIRD_PARTY_PREJOIN_BUTTONS } from '../../../config/constants';
  12. import { withPixelLineHeight } from '../../../styles/functions.web';
  13. import Tooltip from '../../../tooltip/components/Tooltip';
  14. import { isPreCallTestEnabled } from '../../functions';
  15. import ConnectionStatus from './ConnectionStatus';
  16. import Preview from './Preview';
  17. import RecordingWarning from './RecordingWarning';
  18. import UnsafeRoomWarning from './UnsafeRoomWarning';
  19. interface IProps {
  20. /**
  21. * The list of toolbar buttons to render.
  22. */
  23. _buttons: Array<string>;
  24. /**
  25. * Determine if pre call test is enabled.
  26. */
  27. _isPreCallTestEnabled?: boolean;
  28. /**
  29. * The branding background of the premeeting screen(lobby/prejoin).
  30. */
  31. _premeetingBackground: string;
  32. /**
  33. * The name of the meeting that is about to be joined.
  34. */
  35. _roomName: string;
  36. /**
  37. * Children component(s) to be rendered on the screen.
  38. */
  39. children?: ReactNode;
  40. /**
  41. * Additional CSS class names to set on the icon container.
  42. */
  43. className?: string;
  44. /**
  45. * The name of the participant.
  46. */
  47. name?: string;
  48. /**
  49. * Indicates whether the copy url button should be shown.
  50. */
  51. showCopyUrlButton?: boolean;
  52. /**
  53. * Indicates whether the device status should be shown.
  54. */
  55. showDeviceStatus: boolean;
  56. /**
  57. * Indicates whether to display the recording warning.
  58. */
  59. showRecordingWarning?: boolean;
  60. /**
  61. * If should show unsafe room warning when joining.
  62. */
  63. showUnsafeRoomWarning?: boolean;
  64. /**
  65. * The 'Skip prejoin' button to be rendered (if any).
  66. */
  67. skipPrejoinButton?: ReactNode;
  68. /**
  69. * Whether it's used in the 3rdParty prejoin screen or not.
  70. */
  71. thirdParty?: boolean;
  72. /**
  73. * Title of the screen.
  74. */
  75. title?: string;
  76. /**
  77. * True if the preview overlay should be muted, false otherwise.
  78. */
  79. videoMuted?: boolean;
  80. /**
  81. * The video track to render as preview (if omitted, the default local track will be rendered).
  82. */
  83. videoTrack?: Object;
  84. }
  85. const useStyles = makeStyles()(theme => {
  86. return {
  87. container: {
  88. height: '100%',
  89. position: 'absolute',
  90. inset: '0 0 0 0',
  91. display: 'flex',
  92. backgroundColor: theme.palette.ui01,
  93. zIndex: 252,
  94. '@media (max-width: 720px)': {
  95. flexDirection: 'column-reverse'
  96. }
  97. },
  98. content: {
  99. display: 'flex',
  100. flexDirection: 'column',
  101. alignItems: 'center',
  102. flexShrink: 0,
  103. boxSizing: 'border-box',
  104. margin: '0 48px',
  105. padding: '24px 0 16px',
  106. position: 'relative',
  107. width: '300px',
  108. height: '100%',
  109. zIndex: 252,
  110. '@media (max-width: 720px)': {
  111. height: 'auto',
  112. margin: '0 auto'
  113. },
  114. // mobile phone landscape
  115. '@media (max-width: 420px)': {
  116. padding: '16px 16px 0 16px',
  117. width: '100%'
  118. },
  119. '@media (max-width: 400px)': {
  120. padding: '16px'
  121. }
  122. },
  123. contentControls: {
  124. display: 'flex',
  125. flexDirection: 'column',
  126. alignItems: 'center',
  127. margin: 'auto',
  128. width: '100%'
  129. },
  130. title: {
  131. ...withPixelLineHeight(theme.typography.heading4),
  132. color: `${theme.palette.text01}!important`,
  133. marginBottom: theme.spacing(3),
  134. textAlign: 'center',
  135. '@media (max-width: 400px)': {
  136. display: 'none'
  137. }
  138. },
  139. roomNameContainer: {
  140. width: '100%',
  141. textAlign: 'center',
  142. marginBottom: theme.spacing(4)
  143. },
  144. roomName: {
  145. ...withPixelLineHeight(theme.typography.heading5),
  146. color: theme.palette.text01,
  147. display: 'inline-block',
  148. overflow: 'hidden',
  149. textOverflow: 'ellipsis',
  150. whiteSpace: 'nowrap',
  151. maxWidth: '100%',
  152. }
  153. };
  154. });
  155. const PreMeetingScreen = ({
  156. _buttons,
  157. _isPreCallTestEnabled,
  158. _premeetingBackground,
  159. _roomName,
  160. children,
  161. className,
  162. showDeviceStatus,
  163. showRecordingWarning,
  164. showUnsafeRoomWarning,
  165. skipPrejoinButton,
  166. title,
  167. videoMuted,
  168. videoTrack
  169. }: IProps) => {
  170. const { classes } = useStyles();
  171. const style = _premeetingBackground ? {
  172. background: _premeetingBackground,
  173. backgroundPosition: 'center',
  174. backgroundSize: 'cover'
  175. } : {};
  176. const roomNameRef = useRef<HTMLSpanElement | null>(null);
  177. const [ isOverflowing, setIsOverflowing ] = useState(false);
  178. useEffect(() => {
  179. if (roomNameRef.current) {
  180. const element = roomNameRef.current;
  181. const elementStyles = window.getComputedStyle(element);
  182. const elementWidth = Math.floor(parseFloat(elementStyles.width));
  183. setIsOverflowing(element.scrollWidth > elementWidth + 1);
  184. }
  185. }, [ _roomName ]);
  186. return (
  187. <div className = { clsx('premeeting-screen', classes.container, className) }>
  188. <div style = { style }>
  189. <div className = { classes.content }>
  190. {_isPreCallTestEnabled && <ConnectionStatus />}
  191. <div className = { classes.contentControls }>
  192. <h1 className = { classes.title }>
  193. {title}
  194. </h1>
  195. {_roomName && (
  196. <span className = { classes.roomNameContainer }>
  197. {isOverflowing ? (
  198. <Tooltip content = { _roomName }>
  199. <span
  200. className = { classes.roomName }
  201. ref = { roomNameRef }>
  202. {_roomName}
  203. </span>
  204. </Tooltip>
  205. ) : (
  206. <span
  207. className = { classes.roomName }
  208. ref = { roomNameRef }>
  209. {_roomName}
  210. </span>
  211. )}
  212. </span>
  213. )}
  214. {children}
  215. {_buttons.length && <Toolbox toolbarButtons = { _buttons } />}
  216. {skipPrejoinButton}
  217. {showUnsafeRoomWarning && <UnsafeRoomWarning />}
  218. {showDeviceStatus && <DeviceStatus />}
  219. {showRecordingWarning && <RecordingWarning />}
  220. </div>
  221. </div>
  222. </div>
  223. <Preview
  224. videoMuted = { videoMuted }
  225. videoTrack = { videoTrack } />
  226. </div>
  227. );
  228. };
  229. /**
  230. * Maps (parts of) the redux state to the React {@code Component} props.
  231. *
  232. * @param {Object} state - The redux state.
  233. * @param {Object} ownProps - The props passed to the component.
  234. * @returns {Object}
  235. */
  236. function mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
  237. const { hiddenPremeetingButtons } = state['features/base/config'];
  238. const { toolbarButtons } = state['features/toolbox'];
  239. const premeetingButtons = (ownProps.thirdParty
  240. ? THIRD_PARTY_PREJOIN_BUTTONS
  241. : PREMEETING_BUTTONS).filter((b: any) => !(hiddenPremeetingButtons || []).includes(b));
  242. const { premeetingBackground } = state['features/dynamic-branding'];
  243. return {
  244. // For keeping backwards compat.: if we pass an empty hiddenPremeetingButtons
  245. // array through external api, we have all prejoin buttons present on premeeting
  246. // screen regardless of passed values into toolbarButtons config overwrite.
  247. // If hiddenPremeetingButtons is missing, we hide the buttons according to
  248. // toolbarButtons config overwrite.
  249. _buttons: hiddenPremeetingButtons
  250. ? premeetingButtons
  251. : premeetingButtons.filter(b => isButtonEnabled(b, toolbarButtons)),
  252. _isPreCallTestEnabled: isPreCallTestEnabled(state),
  253. _premeetingBackground: premeetingBackground,
  254. _roomName: isRoomNameEnabled(state) ? getConferenceName(state) : ''
  255. };
  256. }
  257. export default connect(mapStateToProps)(PreMeetingScreen);