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.

AbstractApp.js 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. import React, { Component } from 'react';
  2. import { Provider } from 'react-redux';
  3. import {
  4. localParticipantJoined,
  5. localParticipantLeft
  6. } from '../../base/participants';
  7. import {
  8. appNavigate,
  9. appWillMount,
  10. appWillUnmount
  11. } from '../actions';
  12. /**
  13. * Base (abstract) class for main App component.
  14. *
  15. * @abstract
  16. */
  17. export class AbstractApp extends Component {
  18. /**
  19. * AbstractApp component's property types.
  20. *
  21. * @static
  22. */
  23. static propTypes = {
  24. config: React.PropTypes.object,
  25. store: React.PropTypes.object,
  26. /**
  27. * The URL, if any, with which the app was launched.
  28. */
  29. url: React.PropTypes.string
  30. }
  31. /**
  32. * Initializes a new App instance.
  33. *
  34. * @param {Object} props - The read-only React Component props with which
  35. * the new instance is to be initialized.
  36. */
  37. constructor(props) {
  38. super(props);
  39. this.state = {
  40. /**
  41. * The Route rendered by this App.
  42. *
  43. * @type {Route}
  44. */
  45. route: undefined
  46. };
  47. }
  48. /**
  49. * Init lib-jitsi-meet and create local participant when component is going
  50. * to be mounted.
  51. *
  52. * @inheritdoc
  53. */
  54. componentWillMount() {
  55. const dispatch = this.props.store.dispatch;
  56. dispatch(appWillMount(this));
  57. dispatch(localParticipantJoined());
  58. this._openURL(this._getDefaultURL());
  59. }
  60. /**
  61. * Dispose lib-jitsi-meet and remove local participant when component is
  62. * going to be unmounted.
  63. *
  64. * @inheritdoc
  65. */
  66. componentWillUnmount() {
  67. const dispatch = this.props.store.dispatch;
  68. dispatch(localParticipantLeft());
  69. dispatch(appWillUnmount(this));
  70. }
  71. /**
  72. * Implements React's {@link Component#render()}.
  73. *
  74. * @inheritdoc
  75. * @returns {ReactElement}
  76. */
  77. render() {
  78. const route = this.state.route;
  79. if (route) {
  80. return (
  81. <Provider store = { this.props.store }>
  82. {
  83. this._createElement(route.component)
  84. }
  85. </Provider>
  86. );
  87. }
  88. return null;
  89. }
  90. /**
  91. * Create a ReactElement from the specified component, the specified props
  92. * and the props of this AbstractApp which are suitable for propagation to
  93. * the children of this Component.
  94. *
  95. * @param {Component} component - The component from which the ReactElement
  96. * is to be created.
  97. * @param {Object} props - The read-only React Component props with which
  98. * the ReactElement is to be initialized.
  99. * @returns {ReactElement}
  100. * @protected
  101. */
  102. _createElement(component, props) {
  103. /* eslint-disable no-unused-vars, lines-around-comment */
  104. const {
  105. // Don't propagate the config prop(erty) because the config is
  106. // stored inside the Redux state and, thus, is visible to the
  107. // children anyway.
  108. config,
  109. // Don't propagate the dispatch and store props because they usually
  110. // come from react-redux and programmers don't really expect them to
  111. // be inherited but rather explicitly connected.
  112. dispatch, // eslint-disable-line react/prop-types
  113. store,
  114. // The url property was introduced to be consumed entirely by
  115. // AbstractApp.
  116. url,
  117. // The remaining props, if any, are considered suitable for
  118. // propagation to the children of this Component.
  119. ...thisProps
  120. } = this.props;
  121. /* eslint-enable no-unused-vars, lines-around-comment */
  122. // eslint-disable-next-line object-property-newline
  123. return React.createElement(component, { ...thisProps, ...props });
  124. }
  125. /**
  126. * Gets the default URL to be opened when this App mounts.
  127. *
  128. * @private
  129. * @returns {string} The default URL to be opened when this App mounts.
  130. */
  131. _getDefaultURL() {
  132. // If the URL was explicitly specified to the React Component, then open
  133. // it.
  134. let url = this.props.url;
  135. if (url) {
  136. return url;
  137. }
  138. // If the execution environment provides a Location abstraction, then
  139. // this App at already at that location but it must be made aware of the
  140. // fact.
  141. const windowLocation = this._getWindowLocation();
  142. if (windowLocation) {
  143. url = windowLocation.toString();
  144. if (url) {
  145. return url;
  146. }
  147. }
  148. // By default, open the domain configured in the configuration file
  149. // which may be the domain at which the whole server infrastructure is
  150. // deployed.
  151. const config = this.props.config;
  152. if (typeof config === 'object') {
  153. const hosts = config.hosts;
  154. if (typeof hosts === 'object') {
  155. const domain = hosts.domain;
  156. if (domain) {
  157. return `https://${domain}`;
  158. }
  159. }
  160. }
  161. return 'https://meet.jit.si';
  162. }
  163. /**
  164. * Gets a Location object from the window with information about the current
  165. * location of the document. Explicitly defined to allow extenders to
  166. * override because React Native does not usually have a location property
  167. * on its window unless debugging remotely in which case the browser that is
  168. * the remote debugger will provide a location property on the window.
  169. *
  170. * @protected
  171. * @returns {Location} A Location object with information about the current
  172. * location of the document.
  173. */
  174. _getWindowLocation() {
  175. return undefined;
  176. }
  177. /**
  178. * Navigates to a specific Route.
  179. *
  180. * @param {Route} route - The Route to which to navigate.
  181. * @returns {void}
  182. */
  183. _navigate(route) {
  184. let nextState = {
  185. ...this.state,
  186. route
  187. };
  188. // The Web App was using react-router so it utilized react-router's
  189. // onEnter. During the removal of react-router, modifications were
  190. // minimized by preserving the onEnter interface:
  191. // (1) Router would provide its nextState to the Route's onEnter. As the
  192. // role of Router is now this AbstractApp, provide its nextState.
  193. // (2) A replace function would be provided to the Route in case it
  194. // chose to redirect to another path.
  195. this._onRouteEnter(route, nextState, pathname => {
  196. // FIXME In order to minimize the modifications related to the
  197. // removal of react-router, the Web implementation is provided
  198. // bellow because the replace function is used on Web only at the
  199. // time of this writing. Provide a platform-agnostic implementation.
  200. // It should likely find the best Route matching the specified
  201. // pathname and navigate to it.
  202. window.location.pathname = pathname;
  203. // Do not proceed with the route because it chose to redirect to
  204. // another path.
  205. nextState = undefined;
  206. });
  207. nextState && this.setState(nextState);
  208. }
  209. /**
  210. * Notifies this App that a specific Route is about to be rendered.
  211. *
  212. * @param {Route} route - The Route that is about to be rendered.
  213. * @private
  214. * @returns {void}
  215. */
  216. _onRouteEnter(route, ...args) {
  217. // Notify the route that it is about to be entered.
  218. const onEnter = route.onEnter;
  219. if (typeof onEnter === 'function') {
  220. onEnter(...args);
  221. }
  222. }
  223. /**
  224. * Navigates this AbstractApp to (i.e. opens) a specific URL.
  225. *
  226. * @param {string} url - The URL to which to navigate this AbstractApp (i.e.
  227. * the URL to open).
  228. * @protected
  229. * @returns {void}
  230. */
  231. _openURL(url) {
  232. this.props.store.dispatch(appNavigate(url));
  233. }
  234. }