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.

AlwaysOnTop.js 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // @flow
  2. import React, { Component } from 'react';
  3. import Toolbar from './Toolbar';
  4. const { api } = window.alwaysOnTop;
  5. /**
  6. * The timeout in ms for hidding the toolbar.
  7. */
  8. const TOOLBAR_TIMEOUT = 4000;
  9. /**
  10. * The type of the React {@code Component} state of {@link FeedbackButton}.
  11. */
  12. type State = {
  13. avatarURL: string,
  14. displayName: string,
  15. isVideoDisplayed: boolean,
  16. visible: boolean
  17. };
  18. /**
  19. * Represents the always on top page.
  20. *
  21. * @class AlwaysOnTop
  22. * @extends Component
  23. */
  24. export default class AlwaysOnTop extends Component<*, State> {
  25. _hovered: boolean;
  26. /**
  27. * Initializes new AlwaysOnTop instance.
  28. *
  29. * @param {*} props - The read-only properties with which the new instance
  30. * is to be initialized.
  31. */
  32. constructor(props: *) {
  33. super(props);
  34. this.state = {
  35. visible: true,
  36. displayName: '',
  37. isVideoDisplayed: true,
  38. avatarURL: ''
  39. };
  40. // Bind event handlers so they are only bound once per instance.
  41. this._avatarChangedListener = this._avatarChangedListener.bind(this);
  42. this._largeVideoChangedListener
  43. = this._largeVideoChangedListener.bind(this);
  44. this._displayNameChangedListener
  45. = this._displayNameChangedListener.bind(this);
  46. this._mouseMove = this._mouseMove.bind(this);
  47. this._onMouseOut = this._onMouseOut.bind(this);
  48. this._onMouseOver = this._onMouseOver.bind(this);
  49. }
  50. _avatarChangedListener: () => void;
  51. /**
  52. * Handles avatar changed api events.
  53. *
  54. * @returns {void}
  55. */
  56. _avatarChangedListener({ avatarURL, id }) {
  57. if (api._getOnStageParticipant() !== id) {
  58. return;
  59. }
  60. if (avatarURL !== this.state.avatarURL) {
  61. this.setState({ avatarURL });
  62. }
  63. }
  64. _displayNameChangedListener: () => void;
  65. /**
  66. * Handles display name changed api events.
  67. *
  68. * @returns {void}
  69. */
  70. _displayNameChangedListener({ formattedDisplayName, id }) {
  71. if (api._getOnStageParticipant() !== id) {
  72. return;
  73. }
  74. if (formattedDisplayName !== this.state.displayName) {
  75. this.setState({ displayName: formattedDisplayName });
  76. }
  77. }
  78. /**
  79. * Hides the toolbar after a timeout.
  80. *
  81. * @returns {void}
  82. */
  83. _hideToolbarAfterTimeout() {
  84. setTimeout(() => {
  85. if (this._hovered) {
  86. this._hideToolbarAfterTimeout();
  87. return;
  88. }
  89. this.setState({ visible: false });
  90. }, TOOLBAR_TIMEOUT);
  91. }
  92. _largeVideoChangedListener: () => void;
  93. /**
  94. * Handles large video changed api events.
  95. *
  96. * @returns {void}
  97. */
  98. _largeVideoChangedListener() {
  99. const userID = api._getOnStageParticipant();
  100. const displayName = api._getFormattedDisplayName(userID);
  101. const avatarURL = api.getAvatarURL(userID);
  102. const isVideoDisplayed = Boolean(api._getLargeVideo());
  103. this.setState({
  104. avatarURL,
  105. displayName,
  106. isVideoDisplayed
  107. });
  108. }
  109. _mouseMove: () => void;
  110. /**
  111. * Handles mouse move events.
  112. *
  113. * @returns {void}
  114. */
  115. _mouseMove() {
  116. if (!this.state.visible) {
  117. this.setState({ visible: true });
  118. }
  119. }
  120. _onMouseOut: () => void;
  121. /**
  122. * Toolbar mouse out handler.
  123. *
  124. * @returns {void}
  125. */
  126. _onMouseOut() {
  127. this._hovered = false;
  128. }
  129. _onMouseOver: () => void;
  130. /**
  131. * Toolbar mouse over handler.
  132. *
  133. * @returns {void}
  134. */
  135. _onMouseOver() {
  136. this._hovered = true;
  137. }
  138. /**
  139. * Renders display name and avatar for the on stage participant.
  140. *
  141. * @returns {ReactElement}
  142. */
  143. _renderVideoNotAvailableScreen() {
  144. const { avatarURL, displayName, isVideoDisplayed } = this.state;
  145. if (isVideoDisplayed) {
  146. return null;
  147. }
  148. return (
  149. <div id = 'videoNotAvailableScreen'>
  150. {
  151. avatarURL
  152. ? <div id = 'avatarContainer'>
  153. <img
  154. id = 'avatar'
  155. src = { avatarURL } />
  156. </div>
  157. : null
  158. }
  159. <div
  160. className = 'displayname'
  161. id = 'displayname'>
  162. { displayName }
  163. </div>
  164. </div>
  165. );
  166. }
  167. /**
  168. * Sets mouse move listener and initial toolbar timeout.
  169. *
  170. * @inheritdoc
  171. * @returns {void}
  172. */
  173. componentDidMount() {
  174. api.on('largeVideoChanged', this._largeVideoChangedListener);
  175. api.on('displayNameChange', this._displayNameChangedListener);
  176. api.on('avatarChanged', this._avatarChangedListener);
  177. this._largeVideoChangedListener();
  178. window.addEventListener('mousemove', this._mouseMove);
  179. this._hideToolbarAfterTimeout();
  180. }
  181. /**
  182. * Removes all listeners.
  183. *
  184. * @inheritdoc
  185. * @returns {void}
  186. */
  187. componentWillUnmount() {
  188. api.removeListener('largeVideoChanged',
  189. this._largeVideoChangedListener);
  190. api.removeListener('displayNameChange',
  191. this._displayNameChangedListener);
  192. api.removeListener('avatarChanged', this._avatarChangedListener);
  193. window.removeEventListener('mousemove', this._mouseMove);
  194. }
  195. /**
  196. * Sets a timeout to hide the toolbar when the toolbar is shown.
  197. *
  198. * @inheritdoc
  199. * @returns {void}
  200. */
  201. componentWillUpdate(nextProps: *, nextState: State) {
  202. if (!this.state.visible && nextState.visible) {
  203. this._hideToolbarAfterTimeout();
  204. }
  205. }
  206. /**
  207. * Implements React's {@link Component#render()}.
  208. *
  209. * @inheritdoc
  210. * @returns {ReactElement}
  211. */
  212. render() {
  213. return (
  214. <div id = 'alwaysOnTop'>
  215. <Toolbar
  216. className = { this.state.visible ? 'fadeIn' : 'fadeOut' }
  217. onMouseOut = { this._onMouseOut }
  218. onMouseOver = { this._onMouseOver } />
  219. {
  220. this._renderVideoNotAvailableScreen()
  221. }
  222. </div>
  223. );
  224. }
  225. }