2018-05-26 23:51:12 +02:00
|
|
|
// @flow
|
|
|
|
|
|
|
|
import Button from '@atlaskit/button';
|
|
|
|
import { FieldTextStateless } from '@atlaskit/field-text';
|
2018-07-30 03:55:57 +02:00
|
|
|
import { SpotlightTarget } from '@atlaskit/onboarding';
|
2018-06-08 10:51:58 +02:00
|
|
|
import Page from '@atlaskit/page';
|
|
|
|
import { AtlasKitThemeProvider } from '@atlaskit/theme';
|
2018-05-26 23:51:12 +02:00
|
|
|
|
2021-02-23 14:22:54 +01:00
|
|
|
import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random';
|
2018-05-26 23:51:12 +02:00
|
|
|
import React, { Component } from 'react';
|
2020-06-26 13:05:42 +02:00
|
|
|
import { withTranslation } from 'react-i18next';
|
|
|
|
import { compose } from 'redux';
|
2018-06-03 04:36:16 +02:00
|
|
|
import type { Dispatch } from 'redux';
|
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import { push } from 'react-router-redux';
|
2018-05-26 23:51:12 +02:00
|
|
|
|
2018-06-08 10:51:58 +02:00
|
|
|
import { Navbar } from '../../navbar';
|
2018-07-30 03:55:57 +02:00
|
|
|
import { Onboarding, startOnboarding } from '../../onboarding';
|
2018-07-25 13:36:55 +02:00
|
|
|
import { RecentList } from '../../recent-list';
|
2020-06-10 14:21:13 +02:00
|
|
|
import { createConferenceObjectFromURL } from '../../utils';
|
2018-06-08 10:51:58 +02:00
|
|
|
|
2020-05-19 11:43:55 +02:00
|
|
|
import { Body, FieldWrapper, Form, Header, Label, Wrapper } from '../styled';
|
2018-05-26 23:51:12 +02:00
|
|
|
|
|
|
|
type Props = {
|
|
|
|
|
|
|
|
/**
|
2018-06-03 04:36:16 +02:00
|
|
|
* Redux dispatch.
|
2018-05-26 23:51:12 +02:00
|
|
|
*/
|
2018-06-03 04:36:16 +02:00
|
|
|
dispatch: Dispatch<*>;
|
2018-07-08 08:37:31 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* React Router location object.
|
|
|
|
*/
|
|
|
|
location: Object;
|
2020-06-26 13:05:42 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* I18next translate function.
|
|
|
|
*/
|
|
|
|
t: Function;
|
2018-05-26 23:51:12 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
type State = {
|
|
|
|
|
2020-05-19 11:43:55 +02:00
|
|
|
/**
|
|
|
|
* Timer for animating the room name geneeration.
|
|
|
|
*/
|
|
|
|
animateTimeoutId: ?TimeoutID,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generated room name.
|
|
|
|
*/
|
|
|
|
generatedRoomname: string,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current room name placeholder.
|
|
|
|
*/
|
|
|
|
roomPlaceholder: string,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Timer for re-generating a new room name.
|
|
|
|
*/
|
|
|
|
updateTimeoutId: ?TimeoutID,
|
|
|
|
|
2018-05-26 23:51:12 +02:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-06-03 04:36:16 +02:00
|
|
|
class Welcome extends Component<Props, State> {
|
2018-05-26 23:51:12 +02:00
|
|
|
/**
|
|
|
|
* Initializes a new {@code Welcome} instance.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
constructor(props: Props) {
|
|
|
|
super(props);
|
|
|
|
|
2018-07-19 14:16:38 +02:00
|
|
|
// Initialize url value in state if passed using location state object.
|
2018-07-08 08:37:31 +02:00
|
|
|
let url = '';
|
|
|
|
|
|
|
|
// Check and parse url if exists in location state.
|
|
|
|
if (props.location.state) {
|
|
|
|
const { room, serverURL } = props.location.state;
|
|
|
|
|
|
|
|
if (room && serverURL) {
|
|
|
|
url = `${serverURL}/${room}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-19 11:43:55 +02:00
|
|
|
this.state = {
|
|
|
|
animateTimeoutId: undefined,
|
|
|
|
generatedRoomname: '',
|
|
|
|
roomPlaceholder: '',
|
|
|
|
updateTimeoutId: undefined,
|
|
|
|
url
|
|
|
|
};
|
2018-07-19 14:16:38 +02:00
|
|
|
|
|
|
|
// Bind event handlers.
|
2020-05-19 11:43:55 +02:00
|
|
|
this._animateRoomnameChanging = this._animateRoomnameChanging.bind(this);
|
2018-07-19 14:16:38 +02:00
|
|
|
this._onURLChange = this._onURLChange.bind(this);
|
|
|
|
this._onFormSubmit = this._onFormSubmit.bind(this);
|
|
|
|
this._onJoin = this._onJoin.bind(this);
|
2020-05-19 11:43:55 +02:00
|
|
|
this._updateRoomname = this._updateRoomname.bind(this);
|
2018-07-08 08:37:31 +02:00
|
|
|
}
|
|
|
|
|
2018-07-30 03:55:57 +02:00
|
|
|
/**
|
|
|
|
* Start Onboarding once component is mounted.
|
2020-05-19 11:43:55 +02:00
|
|
|
* Start generating randdom room names.
|
2018-07-30 03:55:57 +02:00
|
|
|
*
|
|
|
|
* NOTE: It autonatically checks if the onboarding is shown or not.
|
|
|
|
*
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
componentDidMount() {
|
|
|
|
this.props.dispatch(startOnboarding('welcome-page'));
|
2020-05-19 11:43:55 +02:00
|
|
|
|
|
|
|
this._updateRoomname();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stop all timers when unmounting.
|
|
|
|
*
|
|
|
|
* @returns {voidd}
|
|
|
|
*/
|
|
|
|
componentWillUnmount() {
|
|
|
|
this._clearTimeouts();
|
2018-07-30 03:55:57 +02:00
|
|
|
}
|
|
|
|
|
2018-05-26 23:51:12 +02:00
|
|
|
/**
|
|
|
|
* Render function of component.
|
|
|
|
*
|
2018-06-07 09:47:42 +02:00
|
|
|
* @returns {ReactElement}
|
2018-05-26 23:51:12 +02:00
|
|
|
*/
|
|
|
|
render() {
|
|
|
|
return (
|
2018-06-08 10:51:58 +02:00
|
|
|
<Page navigation = { <Navbar /> }>
|
|
|
|
<AtlasKitThemeProvider mode = 'light'>
|
|
|
|
<Wrapper>
|
2018-08-20 09:19:46 +02:00
|
|
|
{ this._renderHeader() }
|
|
|
|
{ this._renderBody() }
|
2018-07-30 03:55:57 +02:00
|
|
|
<Onboarding section = 'welcome-page' />
|
2018-06-08 10:51:58 +02:00
|
|
|
</Wrapper>
|
|
|
|
</AtlasKitThemeProvider>
|
|
|
|
</Page>
|
2018-05-26 23:51:12 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-05-19 11:43:55 +02:00
|
|
|
_animateRoomnameChanging: (string) => void;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Animates the changing of the room name.
|
|
|
|
*
|
|
|
|
* @param {string} word - The part of room name that should be added to
|
|
|
|
* placeholder.
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_animateRoomnameChanging(word: string) {
|
|
|
|
let animateTimeoutId;
|
|
|
|
const roomPlaceholder = this.state.roomPlaceholder + word.substr(0, 1);
|
|
|
|
|
|
|
|
if (word.length > 1) {
|
|
|
|
animateTimeoutId
|
|
|
|
= setTimeout(
|
|
|
|
() => {
|
|
|
|
this._animateRoomnameChanging(
|
|
|
|
word.substring(1, word.length));
|
|
|
|
},
|
|
|
|
70);
|
|
|
|
}
|
|
|
|
this.setState({
|
|
|
|
animateTimeoutId,
|
|
|
|
roomPlaceholder
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Method that clears timeouts for animations and updates of room name.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_clearTimeouts() {
|
|
|
|
clearTimeout(this.state.animateTimeoutId);
|
|
|
|
clearTimeout(this.state.updateTimeoutId);
|
|
|
|
}
|
|
|
|
|
2018-05-26 23:51:12 +02:00
|
|
|
_onFormSubmit: (*) => void;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prevents submission of the form and delegates the join logic.
|
2018-06-07 09:47:42 +02:00
|
|
|
*
|
|
|
|
* @param {Event} event - Event by which this function is called.
|
|
|
|
* @returns {void}
|
2018-05-26 23:51:12 +02:00
|
|
|
*/
|
|
|
|
_onFormSubmit(event: Event) {
|
|
|
|
event.preventDefault();
|
|
|
|
this._onJoin();
|
|
|
|
}
|
|
|
|
|
|
|
|
_onJoin: (*) => void;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Redirect and join conference.
|
2018-06-07 09:47:42 +02:00
|
|
|
*
|
|
|
|
* @returns {void}
|
2018-05-26 23:51:12 +02:00
|
|
|
*/
|
|
|
|
_onJoin() {
|
2020-05-19 11:43:55 +02:00
|
|
|
const inputURL = this.state.url || this.state.generatedRoomname;
|
2020-06-10 14:21:13 +02:00
|
|
|
const conference = createConferenceObjectFromURL(inputURL);
|
2018-05-26 23:51:12 +02:00
|
|
|
|
2020-06-10 14:21:13 +02:00
|
|
|
// Don't navigate if conference couldn't be created
|
|
|
|
if (!conference) {
|
2018-06-25 10:22:15 +02:00
|
|
|
return;
|
2018-05-26 23:51:12 +02:00
|
|
|
}
|
2018-06-25 10:22:15 +02:00
|
|
|
|
2020-06-10 14:21:13 +02:00
|
|
|
this.props.dispatch(push('/conference', conference));
|
2018-05-26 23:51:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_onURLChange: (*) => void;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Keeps URL input value and URL in state in sync.
|
2018-06-07 09:47:42 +02:00
|
|
|
*
|
|
|
|
* @param {SyntheticInputEvent<HTMLInputElement>} event - Event by which
|
|
|
|
* this function is called.
|
|
|
|
* @returns {void}
|
2018-05-26 23:51:12 +02:00
|
|
|
*/
|
|
|
|
_onURLChange(event: SyntheticInputEvent<HTMLInputElement>) {
|
|
|
|
this.setState({
|
|
|
|
url: event.currentTarget.value
|
|
|
|
});
|
|
|
|
}
|
2018-08-20 09:19:46 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Renders the body for the welcome page.
|
|
|
|
*
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
_renderBody() {
|
|
|
|
return (
|
|
|
|
<Body>
|
|
|
|
<RecentList />
|
|
|
|
</Body>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Renders the header for the welcome page.
|
|
|
|
*
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
_renderHeader() {
|
|
|
|
const locationState = this.props.location.state;
|
|
|
|
const locationError = locationState && locationState.error;
|
2020-06-26 13:05:42 +02:00
|
|
|
const { t } = this.props;
|
2018-08-20 09:19:46 +02:00
|
|
|
|
|
|
|
return (
|
|
|
|
<Header>
|
|
|
|
<SpotlightTarget name = 'conference-url'>
|
|
|
|
<Form onSubmit = { this._onFormSubmit }>
|
2020-06-26 13:05:42 +02:00
|
|
|
<Label>{ t('enterConferenceNameOrUrl') } </Label>
|
2020-05-19 11:43:55 +02:00
|
|
|
<FieldWrapper>
|
|
|
|
<FieldTextStateless
|
|
|
|
autoFocus = { true }
|
|
|
|
isInvalid = { locationError }
|
|
|
|
isLabelHidden = { true }
|
|
|
|
onChange = { this._onURLChange }
|
|
|
|
placeholder = { this.state.roomPlaceholder }
|
|
|
|
shouldFitContainer = { true }
|
|
|
|
type = 'text'
|
|
|
|
value = { this.state.url } />
|
|
|
|
<Button
|
|
|
|
appearance = 'primary'
|
|
|
|
onClick = { this._onJoin }
|
|
|
|
type = 'button'>
|
2020-06-26 13:05:42 +02:00
|
|
|
{ t('go') }
|
2020-05-19 11:43:55 +02:00
|
|
|
</Button>
|
|
|
|
</FieldWrapper>
|
2018-08-20 09:19:46 +02:00
|
|
|
</Form>
|
|
|
|
</SpotlightTarget>
|
|
|
|
</Header>
|
|
|
|
);
|
|
|
|
}
|
2020-05-19 11:43:55 +02:00
|
|
|
|
|
|
|
_updateRoomname: () => void;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Triggers the generation of a new room name and initiates an animation of
|
|
|
|
* its changing.
|
|
|
|
*
|
|
|
|
* @protected
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_updateRoomname() {
|
|
|
|
const generatedRoomname = generateRoomWithoutSeparator();
|
|
|
|
const roomPlaceholder = '';
|
|
|
|
const updateTimeoutId = setTimeout(this._updateRoomname, 10000);
|
|
|
|
|
|
|
|
this._clearTimeouts();
|
|
|
|
this.setState(
|
|
|
|
{
|
|
|
|
generatedRoomname,
|
|
|
|
roomPlaceholder,
|
|
|
|
updateTimeoutId
|
|
|
|
},
|
|
|
|
() => this._animateRoomnameChanging(generatedRoomname));
|
|
|
|
}
|
2018-05-26 23:51:12 +02:00
|
|
|
}
|
2018-06-03 04:36:16 +02:00
|
|
|
|
2020-06-26 13:05:42 +02:00
|
|
|
export default compose(connect(), withTranslation())(Welcome);
|