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.

Whiteboard.tsx 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import { ExcalidrawApp } from '@jitsi/excalidraw';
  2. import clsx from 'clsx';
  3. import React, { useCallback, useEffect, useRef } from 'react';
  4. import { useSelector } from 'react-redux';
  5. // @ts-expect-error
  6. import Filmstrip from '../../../../../modules/UI/videolayout/Filmstrip';
  7. import { IReduxState } from '../../../app/types';
  8. import { translate } from '../../../base/i18n/functions';
  9. import { getLocalParticipant } from '../../../base/participants/functions';
  10. import { getVerticalViewMaxWidth } from '../../../filmstrip/functions.web';
  11. import { getToolboxHeight } from '../../../toolbox/functions.web';
  12. import { shouldDisplayTileView } from '../../../video-layout/functions.any';
  13. import { WHITEBOARD_UI_OPTIONS } from '../../constants';
  14. import {
  15. getCollabDetails,
  16. getCollabServerUrl,
  17. isWhiteboardOpen,
  18. isWhiteboardVisible
  19. } from '../../functions';
  20. /**
  21. * Space taken by meeting elements like the subject and the watermark.
  22. */
  23. const HEIGHT_OFFSET = 80;
  24. interface IDimensions {
  25. /* The height of the component. */
  26. height: string;
  27. /* The width of the component. */
  28. width: string;
  29. }
  30. /**
  31. * The type of the React {@link Component} props of {@link Whiteboard}.
  32. */
  33. type Props = {
  34. /**
  35. * Invoked to obtain translated strings.
  36. */
  37. t: Function;
  38. };
  39. /**
  40. * The Whiteboard component.
  41. *
  42. * @param {Props} props - The React props passed to this component.
  43. * @returns {JSX.Element} - The React component.
  44. */
  45. const Whiteboard = (props: Props): JSX.Element => {
  46. const excalidrawRef = useRef<any>(null);
  47. const collabAPIRef = useRef<any>(null);
  48. const isOpen = useSelector(isWhiteboardOpen);
  49. const isVisible = useSelector(isWhiteboardVisible);
  50. const isInTileView = useSelector(shouldDisplayTileView);
  51. const { clientHeight, clientWidth } = useSelector((state: IReduxState) => state['features/base/responsive-ui']);
  52. const { visible: filmstripVisible, isResizing } = useSelector((state: IReduxState) => state['features/filmstrip']);
  53. const filmstripWidth: number = useSelector(getVerticalViewMaxWidth);
  54. const collabDetails = useSelector(getCollabDetails);
  55. const collabServerUrl = useSelector(getCollabServerUrl);
  56. const { defaultRemoteDisplayName } = useSelector((state: IReduxState) => state['features/base/config']);
  57. const localParticipantName = useSelector(getLocalParticipant)?.name || defaultRemoteDisplayName || 'Fellow Jitster';
  58. useEffect(() => {
  59. if (!collabAPIRef.current) {
  60. return;
  61. }
  62. collabAPIRef.current.setUsername(localParticipantName);
  63. }, [ localParticipantName ]);
  64. /**
  65. * Computes the width and the height of the component.
  66. *
  67. * @returns {IDimensions} - The dimensions of the component.
  68. */
  69. const getDimensions = (): IDimensions => {
  70. let width: number;
  71. let height: number;
  72. if (interfaceConfig.VERTICAL_FILMSTRIP) {
  73. if (filmstripVisible) {
  74. width = clientWidth - filmstripWidth;
  75. } else {
  76. width = clientWidth;
  77. }
  78. height = clientHeight - getToolboxHeight();
  79. } else {
  80. if (filmstripVisible) {
  81. height = clientHeight - Filmstrip.getFilmstripHeight();
  82. } else {
  83. height = clientHeight;
  84. }
  85. width = clientWidth;
  86. }
  87. return {
  88. width: `${width}px`,
  89. height: `${height - HEIGHT_OFFSET}px`
  90. };
  91. };
  92. const getCollabAPI = useCallback(collabAPI => {
  93. if (collabAPIRef.current) {
  94. return;
  95. }
  96. collabAPIRef.current = collabAPI;
  97. collabAPIRef.current.setUsername(localParticipantName);
  98. }, [ localParticipantName ]);
  99. return (
  100. <div
  101. className = { clsx(
  102. isResizing && 'disable-pointer',
  103. 'whiteboard-container'
  104. ) }
  105. style = {{
  106. ...getDimensions(),
  107. marginTop: `${HEIGHT_OFFSET}px`,
  108. display: `${isInTileView || !isVisible ? 'none' : 'block'}`
  109. }}>
  110. {
  111. isOpen && (
  112. <div className = 'excalidraw-wrapper'>
  113. {/*
  114. * Excalidraw renders a few lvl 2 headings. This is
  115. * quite fortunate, because we actually use lvl 1
  116. * headings to mark the big sections of our app. So make
  117. * sure to mark the Excalidraw context with a lvl 1
  118. * heading before showing the whiteboard.
  119. */
  120. <span
  121. aria-level = { 1 }
  122. className = 'sr-only'
  123. role = 'heading'>
  124. { props.t('whiteboard.accessibilityLabel.heading') }
  125. </span>
  126. }
  127. <ExcalidrawApp
  128. collabDetails = { collabDetails }
  129. collabServerUrl = { collabServerUrl }
  130. excalidraw = {{
  131. isCollaborating: true,
  132. // @ts-ignore
  133. ref: excalidrawRef,
  134. theme: 'light',
  135. UIOptions: WHITEBOARD_UI_OPTIONS
  136. }}
  137. getCollabAPI = { getCollabAPI } />
  138. </div>
  139. )
  140. }
  141. </div>
  142. );
  143. };
  144. export default translate(Whiteboard);