diff --git a/.flowconfig b/.flowconfig
index 1c8ac50..01618f8 100644
--- a/.flowconfig
+++ b/.flowconfig
@@ -1,6 +1,7 @@
[ignore]
-.*/node_modules/.*
+.*/node_modules/@atlaskit/.*/*.js.flow
.*/build/.*
+.*/node_modules/electron-packager/test/fixtures/infer-malformed-json/.*
[include]
diff --git a/app/features/app/components/App.js b/app/features/app/components/App.js
index 944caa6..92498e3 100644
--- a/app/features/app/components/App.js
+++ b/app/features/app/components/App.js
@@ -1,9 +1,13 @@
// @flow
+import { AtlasKitThemeProvider } from '@atlaskit/theme';
+
import React, { Component } from 'react';
+import { HashRouter as Router, Route, Switch } from 'react-router-dom';
import { Conference } from '../../conference';
import config from '../../config';
+import { Welcome } from '../../welcome';
/**
* Main component encapsulating the entire application.
@@ -28,7 +32,19 @@ export default class App extends Component<*> {
*/
render() {
return (
-
+
+
+
+
+
+
+
+
);
}
}
diff --git a/app/features/conference/components/Conference.js b/app/features/conference/components/Conference.js
index 69d09bc..f27d225 100644
--- a/app/features/conference/components/Conference.js
+++ b/app/features/conference/components/Conference.js
@@ -1,59 +1,120 @@
// @flow
-import { Component } from 'react';
+import React, { Component } from 'react';
import {
RemoteControl,
setupScreenSharingForWindow,
setupAlwaysOnTopRender,
setupWiFiStats
-
- // $FlowFixMe
} from 'jitsi-meet-electron-utils';
import config from '../../config';
+import { Wrapper } from '../styled';
+
+type Props = {
+
+ /**
+ * React Router history object.
+ * This contains implementations for managing session history.
+ */
+ history: Object;
+
+ /**
+ * React Router match object.
+ * This contains parameters passed through component.
+ */
+ match: Object;
+};
+
/**
* Conference component.
*/
-export default class Conference extends Component<*> {
+export default class Conference extends Component {
/**
- * Attach the script
+ * Reference to the element of this component.
+ */
+ _ref: Object;
+
+ /**
+ * External API object.
+ */
+ _api: Object;
+
+ /**
+ * Initializes a new {@code Conference} instance.
+ *
+ * @inheritdoc
+ */
+ constructor() {
+ super();
+
+ this._ref = React.createRef();
+ }
+
+ /**
+ * Attach the script to this component.
*/
componentDidMount() {
+ const parentNode = this._ref.current;
+ const room = this.props.match.params.room;
+ const domain = this.props.match.params.domain || config.defaultDomain;
+
const script = document.createElement('script');
script.async = true;
- script.onload = this._onScriptLoad;
- script.onerror = console.error;
- script.src = `https://${config.defaultDomain}/external_api.js`;
+ script.onload = () => this._onScriptLoad(parentNode, room, domain);
+ script.onerror = () => this._navigateToHome();
+ script.src = `https://${domain}/external_api.js`;
- // $FlowFixMe
- document.head.appendChild(script);
+ this._ref.current.appendChild(script);
}
+ /**
+ * Remove conference on unmounting.
+ */
+ componentWillUnmount() {
+ if (this._api) {
+ this._api.dispose();
+ }
+ }
/**
- * Render function of component.
+ * Implements React's {@link Component#render()}.
*
- * @return {ReactElement}
+ * @inheritdoc
+ * @returns {ReactElement}
*/
render() {
- return null;
+ return ;
}
/**
- * When the script is loaded attach utils from jitsi-meet-electron-utils
+ * Navigates to home screen (Welcome).
*/
- _onScriptLoad() {
+ _navigateToHome() {
+ this.props.history.push('/');
+ }
+
+ /**
+ * When the script is loaded create the iframe element in this component
+ * and attach utils from jitsi-meet-electron-utils.
+ */
+ _onScriptLoad(parentNode: Object, roomName: string, domain: string) {
const JitsiMeetExternalAPI = window.JitsiMeetExternalAPI;
- const api = new JitsiMeetExternalAPI(config.defaultDomain);
- const iframe = api.getIFrame();
+ this._api = new JitsiMeetExternalAPI(domain, {
+ parentNode,
+ roomName
+ });
+ const iframe = this._api.getIFrame();
setupScreenSharingForWindow(iframe);
new RemoteControl(iframe); // eslint-disable-line no-new
- setupAlwaysOnTopRender(api);
+ setupAlwaysOnTopRender(this._api);
setupWiFiStats(iframe);
+
+ this._api.on('readyToClose', () => this._navigateToHome());
}
}
diff --git a/app/features/conference/index.js b/app/features/conference/index.js
index 07635cb..213d6c1 100644
--- a/app/features/conference/index.js
+++ b/app/features/conference/index.js
@@ -1 +1,3 @@
export * from './components';
+export * from './styled';
+
diff --git a/app/features/conference/styled/Wrapper.js b/app/features/conference/styled/Wrapper.js
new file mode 100644
index 0000000..c2e9850
--- /dev/null
+++ b/app/features/conference/styled/Wrapper.js
@@ -0,0 +1,7 @@
+// @flow
+
+import styled from 'styled-components';
+
+export default styled.div`
+ height: 100vh;
+`;
diff --git a/app/features/conference/styled/index.js b/app/features/conference/styled/index.js
new file mode 100644
index 0000000..50472e0
--- /dev/null
+++ b/app/features/conference/styled/index.js
@@ -0,0 +1 @@
+export { default as Wrapper } from './Wrapper';
diff --git a/app/features/welcome/components/Welcome.js b/app/features/welcome/components/Welcome.js
new file mode 100644
index 0000000..761be85
--- /dev/null
+++ b/app/features/welcome/components/Welcome.js
@@ -0,0 +1,124 @@
+// @flow
+
+import { AtlasKitThemeProvider } from '@atlaskit/theme';
+import Button from '@atlaskit/button';
+import { FieldTextStateless } from '@atlaskit/field-text';
+
+import React, { Component } from 'react';
+import URL from 'url';
+
+import { WelcomeWrapper as Wrapper, Content, Form } from '../styled';
+
+
+type Props = {
+
+ /**
+ * React Router history object.
+ * This contains implementations for managing session history.
+ */
+ history: Object;
+};
+
+type State = {
+
+ /**
+ * URL of the room to join.
+ * If this is not a url it will be treated as room name for default domain.
+ */
+ url: string;
+};
+
+/**
+ * Welcome Component.
+ */
+export default class Welcome extends Component {
+ /**
+ * Initializes a new {@code Welcome} instance.
+ *
+ * @inheritdoc
+ */
+ constructor(props: Props) {
+ super(props);
+
+ this.state = {
+ url: ''
+ };
+
+ // Bind event handlers.
+ this._onURLChange = this._onURLChange.bind(this);
+ this._onFormSubmit = this._onFormSubmit.bind(this);
+ this._onJoin = this._onJoin.bind(this);
+ }
+
+ /**
+ * Render function of component.
+ *
+ * @return {ReactElement}
+ */
+ render() {
+ return (
+
+
+
+
+
+
+
+
+ );
+ }
+
+ _onFormSubmit: (*) => void;
+
+ /**
+ * Prevents submission of the form and delegates the join logic.
+ */
+ _onFormSubmit(event: Event) {
+ event.preventDefault();
+ this._onJoin();
+ }
+
+ _onJoin: (*) => void;
+
+ /**
+ * Redirect and join conference.
+ */
+ _onJoin() {
+ const url = URL.parse(this.state.url);
+
+ // Check if the parsed url is a full url or just room name.
+ if (url.host && url.path) {
+
+ // This will be triggered when the full url is present.
+ this.props.history.push(url.host + url.path);
+ } else {
+
+ // Directly to the the path.
+ this.props.history.push(url.path);
+ }
+ }
+
+ _onURLChange: (*) => void;
+
+ /**
+ * Keeps URL input value and URL in state in sync.
+ */
+ _onURLChange(event: SyntheticInputEvent) {
+ this.setState({
+ url: event.currentTarget.value
+ });
+ }
+}
diff --git a/app/features/welcome/components/index.js b/app/features/welcome/components/index.js
new file mode 100644
index 0000000..091c2f5
--- /dev/null
+++ b/app/features/welcome/components/index.js
@@ -0,0 +1 @@
+export { default as Welcome } from './Welcome';
diff --git a/app/features/welcome/index.js b/app/features/welcome/index.js
new file mode 100644
index 0000000..f0ec1c6
--- /dev/null
+++ b/app/features/welcome/index.js
@@ -0,0 +1,2 @@
+export * from './components';
+export * from './styled';
diff --git a/app/features/welcome/styled/Content.js b/app/features/welcome/styled/Content.js
new file mode 100644
index 0000000..33fcb67
--- /dev/null
+++ b/app/features/welcome/styled/Content.js
@@ -0,0 +1,10 @@
+// @flow
+
+import styled from 'styled-components';
+
+export default styled.div`
+ align-items: center;
+ display: flex;
+ margin: 0 auto;
+ padding: 30px;
+`;
diff --git a/app/features/welcome/styled/Form.js b/app/features/welcome/styled/Form.js
new file mode 100644
index 0000000..dcb562f
--- /dev/null
+++ b/app/features/welcome/styled/Form.js
@@ -0,0 +1,8 @@
+// Flow
+
+import styled from 'styled-components';
+
+export default styled.form`
+ width: 350px;
+ margin: 0 15px;
+`;
diff --git a/app/features/welcome/styled/WelcomeWrapper.js b/app/features/welcome/styled/WelcomeWrapper.js
new file mode 100644
index 0000000..3fe50b7
--- /dev/null
+++ b/app/features/welcome/styled/WelcomeWrapper.js
@@ -0,0 +1,9 @@
+// @flow
+
+import styled from 'styled-components';
+
+export default styled.div`
+ background: linear-gradient(#165ecc,#44A5FF);
+ display: flex;
+ height: 100vh;
+`;
diff --git a/app/features/welcome/styled/index.js b/app/features/welcome/styled/index.js
new file mode 100644
index 0000000..6b132d9
--- /dev/null
+++ b/app/features/welcome/styled/index.js
@@ -0,0 +1,3 @@
+export { default as Content } from './Content';
+export { default as Form } from './Form';
+export { default as WelcomeWrapper } from './WelcomeWrapper';
diff --git a/app/index.html b/app/index.html
index befc6f9..45a0543 100644
--- a/app/index.html
+++ b/app/index.html
@@ -3,8 +3,6 @@