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.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import { ExcalidrawApp } from '@jitsi/excalidraw';
  2. import clsx from 'clsx';
  3. import i18next from 'i18next';
  4. import React, { useCallback, useEffect, useRef } from 'react';
  5. import { WithTranslation } from 'react-i18next';
  6. import { useSelector } from 'react-redux';
  7. // @ts-expect-error
  8. import Filmstrip from '../../../../../modules/UI/videolayout/Filmstrip';
  9. import { IReduxState } from '../../../app/types';
  10. import { translate } from '../../../base/i18n/functions';
  11. import { getLocalParticipant } from '../../../base/participants/functions';
  12. import { getVerticalViewMaxWidth } from '../../../filmstrip/functions.web';
  13. import { getToolboxHeight } from '../../../toolbox/functions.web';
  14. import { shouldDisplayTileView } from '../../../video-layout/functions.any';
  15. import { WHITEBOARD_UI_OPTIONS } from '../../constants';
  16. import {
  17. getCollabDetails,
  18. getCollabServerUrl,
  19. isWhiteboardOpen,
  20. isWhiteboardVisible
  21. } from '../../functions';
  22. /**
  23. * Space taken by meeting elements like the subject and the watermark.
  24. */
  25. const HEIGHT_OFFSET = 80;
  26. interface IDimensions {
  27. /* The height of the component. */
  28. height: string;
  29. /* The width of the component. */
  30. width: string;
  31. }
  32. /**
  33. * The Whiteboard component.
  34. *
  35. * @param {Props} props - The React props passed to this component.
  36. * @returns {JSX.Element} - The React component.
  37. */
  38. const Whiteboard = (props: WithTranslation): JSX.Element => {
  39. const excalidrawRef = useRef<any>(null);
  40. const excalidrawAPIRef = useRef<any>(null);
  41. const collabAPIRef = useRef<any>(null);
  42. const isOpen = useSelector(isWhiteboardOpen);
  43. const isVisible = useSelector(isWhiteboardVisible);
  44. const isInTileView = useSelector(shouldDisplayTileView);
  45. const { clientHeight, clientWidth } = useSelector((state: IReduxState) => state['features/base/responsive-ui']);
  46. const { visible: filmstripVisible, isResizing } = useSelector((state: IReduxState) => state['features/filmstrip']);
  47. const filmstripWidth: number = useSelector(getVerticalViewMaxWidth);
  48. const collabDetails = useSelector(getCollabDetails);
  49. const collabServerUrl = useSelector(getCollabServerUrl);
  50. const { defaultRemoteDisplayName } = useSelector((state: IReduxState) => state['features/base/config']);
  51. const localParticipantName = useSelector(getLocalParticipant)?.name || defaultRemoteDisplayName || 'Fellow Jitster';
  52. useEffect(() => {
  53. if (!collabAPIRef.current) {
  54. return;
  55. }
  56. collabAPIRef.current.setUsername(localParticipantName);
  57. }, [ localParticipantName ]);
  58. /**
  59. * Computes the width and the height of the component.
  60. *
  61. * @returns {IDimensions} - The dimensions of the component.
  62. */
  63. const getDimensions = (): IDimensions => {
  64. let width: number;
  65. let height: number;
  66. if (interfaceConfig.VERTICAL_FILMSTRIP) {
  67. if (filmstripVisible) {
  68. width = clientWidth - filmstripWidth;
  69. } else {
  70. width = clientWidth;
  71. }
  72. height = clientHeight - getToolboxHeight();
  73. } else {
  74. if (filmstripVisible) {
  75. height = clientHeight - Filmstrip.getFilmstripHeight();
  76. } else {
  77. height = clientHeight;
  78. }
  79. width = clientWidth;
  80. }
  81. return {
  82. width: `${width}px`,
  83. height: `${height - HEIGHT_OFFSET}px`
  84. };
  85. };
  86. const getExcalidrawAPI = useCallback(excalidrawAPI => {
  87. if (excalidrawAPIRef.current) {
  88. return;
  89. }
  90. excalidrawAPIRef.current = excalidrawAPI;
  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. langCode: i18next.language,
  133. // @ts-ignore
  134. ref: excalidrawRef,
  135. theme: 'light',
  136. UIOptions: WHITEBOARD_UI_OPTIONS
  137. }}
  138. getCollabAPI = { getCollabAPI }
  139. getExcalidrawAPI = { getExcalidrawAPI } />
  140. </div>
  141. )
  142. }
  143. </div>
  144. );
  145. };
  146. export default translate(Whiteboard);