Introduce internationalisation
This commit is contained in:
parent
1c4f76e3b8
commit
2efa8b057e
|
@ -4,6 +4,7 @@ import Droplist, { Item, Group } from '@atlaskit/droplist';
|
|||
import HelpIcon from '@atlaskit/icon/glyph/question-circle';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
import config from '../../config';
|
||||
import { openExternalLink } from '../../utils';
|
||||
|
@ -20,7 +21,7 @@ type State = {
|
|||
/**
|
||||
* Help button for Navigation Bar.
|
||||
*/
|
||||
export default class HelpButton extends Component< *, State> {
|
||||
class HelpButton extends Component<*, State> {
|
||||
/**
|
||||
* Initializes a new {@code HelpButton} instance.
|
||||
*
|
||||
|
@ -87,6 +88,8 @@ export default class HelpButton extends Component< *, State> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<Droplist
|
||||
isOpen = { this.state.droplistOpen }
|
||||
|
@ -94,27 +97,29 @@ export default class HelpButton extends Component< *, State> {
|
|||
onOpenChange = { this._onOpenChange }
|
||||
position = 'right bottom'
|
||||
trigger = { <HelpIcon /> }>
|
||||
<Group heading = 'Help'>
|
||||
<Group heading = { t('help') } >
|
||||
<Item onActivate = { this._onTermsClick }>
|
||||
Terms
|
||||
{ t('termsLink') }
|
||||
</Item>
|
||||
<Item onActivate = { this._onPrivacyClick }>
|
||||
Privacy
|
||||
{ t('privacyLink') }
|
||||
</Item>
|
||||
<Item onActivate = { this._onSendFeedbackClick }>
|
||||
Send Feedback
|
||||
{ t('sendFeedbackLink') }
|
||||
</Item>
|
||||
<Item onActivate = { this._onAboutClick }>
|
||||
About
|
||||
{ t('aboutLink') }
|
||||
</Item>
|
||||
<Item onActivate = { this._onSourceClick }>
|
||||
Source
|
||||
{ t('sourceLink') }
|
||||
</Item>
|
||||
<Item>
|
||||
Version: { version }
|
||||
{ t('versionLabel', { version }) }
|
||||
</Item>
|
||||
</Group>
|
||||
</Droplist>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation()(HelpButton);
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { Spotlight } from '@atlaskit/onboarding';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { closeDrawer } from '../../navbar';
|
||||
|
||||
import { continueOnboarding } from '../actions';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Always on Top Windows Spotlight Component.
|
||||
*/
|
||||
class AlwaysOnTopWindowSpotlight extends Component<Props, *> {
|
||||
/**
|
||||
* Initializes a new {@code StartMutedTogglesSpotlight} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._next = this._next.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render function of component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Spotlight
|
||||
actions = { [
|
||||
{
|
||||
onClick: this._next,
|
||||
text: 'Next'
|
||||
}
|
||||
] }
|
||||
dialogPlacement = 'top right'
|
||||
target = { 'always-on-top-window' } >
|
||||
You can toggle whether you want to enable the "always-on-top" window,
|
||||
which is displayed when the main window loses focus.
|
||||
This will be applied to all conferences.
|
||||
</Spotlight>
|
||||
);
|
||||
}
|
||||
|
||||
_next: (*) => void;
|
||||
|
||||
/**
|
||||
* Close the spotlight component.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_next() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(continueOnboarding());
|
||||
|
||||
// FIXME: find a better way to do this.
|
||||
setTimeout(() => {
|
||||
dispatch(closeDrawer());
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(AlwaysOnTopWindowSpotlight);
|
|
@ -1,70 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { Spotlight } from '@atlaskit/onboarding';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { continueOnboarding } from '../actions';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Conference URL Spotlight Component.
|
||||
*/
|
||||
class ConferenceURLSpotlight extends Component<Props, *> {
|
||||
/**
|
||||
* Initializes a new {@code ComponentURLSpotlight} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._next = this._next.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render function of component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Spotlight
|
||||
actions = { [
|
||||
{
|
||||
onClick: this._next,
|
||||
text: 'Next'
|
||||
}
|
||||
] }
|
||||
dialogPlacement = 'bottom center'
|
||||
target = { 'conference-url' } >
|
||||
Enter the name (or full URL) of the room you want to join. You
|
||||
may make a name up, just let others know so they enter the same
|
||||
name.
|
||||
</Spotlight>
|
||||
);
|
||||
}
|
||||
|
||||
_next: (*) => void;
|
||||
|
||||
/**
|
||||
* Close the spotlight component.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_next() {
|
||||
this.props.dispatch(continueOnboarding());
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(ConferenceURLSpotlight);
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { Spotlight } from '@atlaskit/onboarding';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { continueOnboarding } from '../actions';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Email Setting Spotlight Component.
|
||||
*/
|
||||
class EmailSettingSpotlight extends Component<Props, *> {
|
||||
/**
|
||||
* Initializes a new {@code EmailSettingSpotlight} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._next = this._next.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render function of component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Spotlight
|
||||
actions = { [
|
||||
{
|
||||
onClick: this._next,
|
||||
text: 'Next'
|
||||
}
|
||||
] }
|
||||
dialogPlacement = 'top right'
|
||||
target = { 'email-setting' } >
|
||||
The email you enter here will be part of your user profile.
|
||||
</Spotlight>
|
||||
);
|
||||
}
|
||||
|
||||
_next: (*) => void;
|
||||
|
||||
/**
|
||||
* Close the spotlight component.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_next() {
|
||||
this.props.dispatch(continueOnboarding());
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(EmailSettingSpotlight);
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { Spotlight } from '@atlaskit/onboarding';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { continueOnboarding } from '../actions';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Name Setting Spotlight Component.
|
||||
*/
|
||||
class NameSettingSpotlight extends Component<Props, *> {
|
||||
/**
|
||||
* Initializes a new {@code NameSettingSpotlight} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._next = this._next.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render function of component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Spotlight
|
||||
actions = { [
|
||||
{
|
||||
onClick: this._next,
|
||||
text: 'Next'
|
||||
}
|
||||
] }
|
||||
dialogPlacement = 'top right'
|
||||
target = { 'name-setting' } >
|
||||
This will be your display name, others will see you with this
|
||||
name.
|
||||
</Spotlight>
|
||||
);
|
||||
}
|
||||
|
||||
_next: (*) => void;
|
||||
|
||||
/**
|
||||
* Close the spotlight component.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_next() {
|
||||
this.props.dispatch(continueOnboarding());
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(NameSettingSpotlight);
|
||||
|
|
@ -38,9 +38,9 @@ class Onboarding extends Component<Props, *> {
|
|||
const steps = onboardingSteps[section];
|
||||
|
||||
if (_activeOnboarding && steps.includes(_activeOnboarding)) {
|
||||
const ActiveOnboarding = onboardingComponents[_activeOnboarding];
|
||||
const { type: ActiveOnboarding, ...props } = onboardingComponents[_activeOnboarding];
|
||||
|
||||
return <ActiveOnboarding />;
|
||||
return <ActiveOnboarding { ...props } />;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
import { Modal } from '@atlaskit/onboarding';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
import { compose } from 'redux';
|
||||
|
||||
import OnboardingModalImage from '../../../images/onboarding.png';
|
||||
|
||||
|
@ -18,6 +20,11 @@ type Props = {
|
|||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
|
||||
/**
|
||||
* I18next translation function.
|
||||
*/
|
||||
t: Function;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -43,21 +50,23 @@ class OnboardingModal extends Component<Props, *> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
actions = { [
|
||||
{
|
||||
onClick: this._next,
|
||||
text: 'Start Tour'
|
||||
text: t('onboarding.startTour')
|
||||
},
|
||||
{
|
||||
onClick: this._skip,
|
||||
text: 'Skip'
|
||||
text: t('onboarding.skip')
|
||||
}
|
||||
] }
|
||||
heading = { `Welcome to ${config.appName}` }
|
||||
heading = { t('onboarding.welcome', { appName: config.appName }) }
|
||||
image = { OnboardingModalImage } >
|
||||
<p> Let us show you around!</p>
|
||||
<p> { t('onboarding.letUsShowYouAround') }</p>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
@ -86,4 +95,4 @@ class OnboardingModal extends Component<Props, *> {
|
|||
|
||||
}
|
||||
|
||||
export default connect()(OnboardingModal);
|
||||
export default compose(connect(), withTranslation())(OnboardingModal);
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
// @flow
|
||||
|
||||
import { Spotlight } from '@atlaskit/onboarding';
|
||||
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { continueOnboarding } from '../actions';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
|
||||
/**
|
||||
* Spotlight dialog placement.
|
||||
*/
|
||||
dialogPlacement: String;
|
||||
|
||||
/**
|
||||
* Callback when "next" clicked.
|
||||
*/
|
||||
onNext: Function;
|
||||
|
||||
/**
|
||||
* I18next translation function.
|
||||
*/
|
||||
t: Function;
|
||||
|
||||
/**
|
||||
* Spotlight target.
|
||||
*/
|
||||
target: String;
|
||||
|
||||
/**
|
||||
* Spotlight text.
|
||||
*/
|
||||
text: String;
|
||||
|
||||
};
|
||||
|
||||
const OnboardingSpotlight = (props: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Spotlight
|
||||
actions = { [
|
||||
{
|
||||
onClick: () => {
|
||||
props.dispatch(continueOnboarding());
|
||||
props.onNext && props.onNext(props);
|
||||
},
|
||||
text: t('onboarding.next')
|
||||
}
|
||||
] }
|
||||
dialogPlacement = { props.dialogPlacement }
|
||||
target = { props.target } >
|
||||
{ t(props.text) }
|
||||
</Spotlight>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default connect()(OnboardingSpotlight);
|
|
@ -1,69 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { Spotlight } from '@atlaskit/onboarding';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { continueOnboarding } from '../actions';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Server Setting Spotlight Component.
|
||||
*/
|
||||
class ServerSettingSpotlight extends Component<Props, *> {
|
||||
/**
|
||||
* Initializes a new {@code ServerSettingSpotlight} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._next = this._next.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render function of component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Spotlight
|
||||
actions = { [
|
||||
{
|
||||
onClick: this._next,
|
||||
text: 'Next'
|
||||
}
|
||||
] }
|
||||
dialogPlacement = 'top right'
|
||||
target = { 'server-setting' } >
|
||||
This will be the server where your conferences will take place.
|
||||
You can use your own, but you don't need to!
|
||||
</Spotlight>
|
||||
);
|
||||
}
|
||||
|
||||
_next: (*) => void;
|
||||
|
||||
/**
|
||||
* Close the spotlight component.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_next() {
|
||||
this.props.dispatch(continueOnboarding());
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(ServerSettingSpotlight);
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { Spotlight } from '@atlaskit/onboarding';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { continueOnboarding } from '../actions';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Server Setting Spotlight Component.
|
||||
*/
|
||||
class ServerTimeoutSpotlight extends Component<Props, *> {
|
||||
/**
|
||||
* Initializes a new {@code ServerSettingSpotlight} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._next = this._next.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render function of component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Spotlight
|
||||
actions = { [
|
||||
{
|
||||
onClick: this._next,
|
||||
text: 'Next'
|
||||
}
|
||||
] }
|
||||
dialogPlacement = 'right top'
|
||||
target = { 'server-timeout' } >
|
||||
Timeout to join a meeting, if the meeting hasn't been joined before the timeout hits, it's cancelled.
|
||||
</Spotlight>
|
||||
);
|
||||
}
|
||||
|
||||
_next: (*) => void;
|
||||
|
||||
/**
|
||||
* Close the spotlight component.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_next() {
|
||||
this.props.dispatch(continueOnboarding());
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(ServerTimeoutSpotlight);
|
|
@ -1,68 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { Spotlight } from '@atlaskit/onboarding';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { openDrawer } from '../../navbar';
|
||||
import { SettingsDrawer } from '../../settings';
|
||||
|
||||
import { continueOnboarding } from '../actions';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Settings Drawer Spotlight Component.
|
||||
*/
|
||||
class SettingsDrawerSpotlight extends Component<Props, *> {
|
||||
/**
|
||||
* Initializes a new {@code SettingsDrawerSpotlight} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._next = this._next.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render function of component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Spotlight
|
||||
dialogPlacement = 'top right'
|
||||
target = { 'settings-drawer-button' }
|
||||
targetOnClick = { this._next }>
|
||||
Click here to open the settings drawer.
|
||||
</Spotlight>
|
||||
);
|
||||
}
|
||||
|
||||
_next: (*) => void;
|
||||
|
||||
/**
|
||||
* Close the spotlight component and opens Settings Drawer and shows
|
||||
* onboarding.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_next() {
|
||||
this.props.dispatch(openDrawer(SettingsDrawer));
|
||||
this.props.dispatch(continueOnboarding());
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(SettingsDrawerSpotlight);
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { Spotlight } from '@atlaskit/onboarding';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { continueOnboarding } from '../actions';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Start Muted Toggles Spotlight Component.
|
||||
*/
|
||||
class StartMutedTogglesSpotlight extends Component<Props, *> {
|
||||
/**
|
||||
* Initializes a new {@code StartMutedTogglesSpotlight} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._next = this._next.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render function of component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Spotlight
|
||||
actions = { [
|
||||
{
|
||||
onClick: this._next,
|
||||
text: 'Next'
|
||||
}
|
||||
] }
|
||||
dialogPlacement = 'top right'
|
||||
target = { 'start-muted-toggles' } >
|
||||
You can toggle if you want to start with your audio or video
|
||||
muted here. This will be applied to all conferences.
|
||||
</Spotlight>
|
||||
);
|
||||
}
|
||||
|
||||
_next: (*) => void;
|
||||
|
||||
/**
|
||||
* Close the spotlight component.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_next() {
|
||||
this.props.dispatch(continueOnboarding());
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(StartMutedTogglesSpotlight);
|
||||
|
|
@ -1,10 +1,3 @@
|
|||
export { default as ConferenceURLSpotlight } from './ConferenceURLSpotlight';
|
||||
export { default as EmailSettingSpotlight } from './EmailSettingSpotlight';
|
||||
export { default as NameSettingSpotlight } from './NameSettingSpotlight';
|
||||
export { default as OnboardingSpotlight } from './OnboardingSpotlight';
|
||||
export { default as Onboarding } from './Onboarding';
|
||||
export { default as OnboardingModal } from './OnboardingModal';
|
||||
export { default as ServerSettingSpotlight } from './ServerSettingSpotlight';
|
||||
export { default as ServerTimeoutSpotlight } from './ServerTimeoutSpotlight';
|
||||
export { default as SettingsDrawerSpotlight } from './SettingsDrawerSpotlight';
|
||||
export { default as StartMutedTogglesSpotlight } from './StartMutedTogglesSpotlight';
|
||||
export { default as AlwaysOnTopWindowSpotlight } from './AlwaysOnTopWindowSpotlight';
|
||||
|
|
|
@ -1,16 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import {
|
||||
OnboardingModal,
|
||||
ConferenceURLSpotlight,
|
||||
SettingsDrawerSpotlight,
|
||||
NameSettingSpotlight,
|
||||
EmailSettingSpotlight,
|
||||
StartMutedTogglesSpotlight,
|
||||
ServerSettingSpotlight,
|
||||
ServerTimeoutSpotlight,
|
||||
AlwaysOnTopWindowSpotlight
|
||||
} from './components';
|
||||
import { OnboardingModal, OnboardingSpotlight } from './components';
|
||||
import { openDrawer, closeDrawer } from '../navbar';
|
||||
import { SettingsDrawer } from '../settings';
|
||||
|
||||
export const advenaceSettingsSteps = [
|
||||
'server-setting',
|
||||
|
@ -33,13 +24,57 @@ export const onboardingSteps = {
|
|||
};
|
||||
|
||||
export const onboardingComponents = {
|
||||
'onboarding-modal': OnboardingModal,
|
||||
'conference-url': ConferenceURLSpotlight,
|
||||
'settings-drawer-button': SettingsDrawerSpotlight,
|
||||
'name-setting': NameSettingSpotlight,
|
||||
'email-setting': EmailSettingSpotlight,
|
||||
'start-muted-toggles': StartMutedTogglesSpotlight,
|
||||
'server-setting': ServerSettingSpotlight,
|
||||
'server-timeout': ServerTimeoutSpotlight,
|
||||
'always-on-top-window': AlwaysOnTopWindowSpotlight
|
||||
'onboarding-modal': { type: OnboardingModal },
|
||||
'conference-url': {
|
||||
type: OnboardingSpotlight,
|
||||
dialogPlacement: 'bottom center',
|
||||
target: 'conference-url',
|
||||
text: 'onboarding.conferenceUrl'
|
||||
},
|
||||
'settings-drawer-button': {
|
||||
type: OnboardingSpotlight,
|
||||
dialogPlacement: 'top right',
|
||||
target: 'settings-drawer-button',
|
||||
text: 'onboarding.settingsDrawerButton',
|
||||
onNext: (props: OnboardingSpotlight.props) => props.dispatch(openDrawer(SettingsDrawer))
|
||||
},
|
||||
'name-setting': {
|
||||
type: OnboardingSpotlight,
|
||||
dialogPlacement: 'top right',
|
||||
target: 'name-setting',
|
||||
text: 'onboarding.nameSetting'
|
||||
},
|
||||
'email-setting': {
|
||||
type: OnboardingSpotlight,
|
||||
dialogPlacement: 'top right',
|
||||
target: 'email-setting',
|
||||
text: 'onboarding.emailSetting'
|
||||
},
|
||||
'start-muted-toggles': {
|
||||
type: OnboardingSpotlight,
|
||||
dialogPlacement: 'top right',
|
||||
target: 'start-muted-toggles',
|
||||
text: 'onboarding.startMutedToggles'
|
||||
},
|
||||
'server-setting': {
|
||||
type: OnboardingSpotlight,
|
||||
dialogPlacement: 'top right',
|
||||
target: 'server-setting',
|
||||
text: 'onboarding.serverSetting'
|
||||
},
|
||||
'server-timeout': {
|
||||
type: OnboardingSpotlight,
|
||||
dialogPlacement: 'top right',
|
||||
target: 'server-timeout',
|
||||
text: 'onboarding.serverTimeout'
|
||||
},
|
||||
'always-on-top-window': {
|
||||
type: OnboardingSpotlight,
|
||||
dialogPlacement: 'top right',
|
||||
target: 'always-on-top-window',
|
||||
text: 'onboarding.alwaysOnTop',
|
||||
onNext: (props: OnboardingSpotlight.props) => setTimeout(() => {
|
||||
props.dispatch(closeDrawer());
|
||||
}, 300)
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { setWindowAlwaysOnTop } from '../actions';
|
||||
|
||||
import ToggleWithLabel from './ToggleWithLabel';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
|
||||
/**
|
||||
* Window Always on Top value in (redux) state.
|
||||
*/
|
||||
_alwaysOnTopWindowEnabled: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Window always open on top placed in Settings Drawer.
|
||||
*/
|
||||
class AlwaysOnTopWindowToggle extends Component<Props> {
|
||||
/**
|
||||
* Initializes a new {@code AlwaysOnTopWindowToggle} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._onAlwaysOnTopWindowToggleChange
|
||||
= this._onAlwaysOnTopWindowToggleChange.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render function of component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<ToggleWithLabel
|
||||
isDefaultChecked = { this.props._alwaysOnTopWindowEnabled }
|
||||
label = 'Always on Top Window'
|
||||
onChange = { this._onAlwaysOnTopWindowToggleChange }
|
||||
value = { this.props._alwaysOnTopWindowEnabled } />
|
||||
);
|
||||
}
|
||||
|
||||
_onAlwaysOnTopWindowToggleChange: (*) => void;
|
||||
|
||||
/**
|
||||
* Toggles alwaysOnTopWindowEnabled.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onAlwaysOnTopWindowToggleChange() {
|
||||
const { _alwaysOnTopWindowEnabled } = this.props;
|
||||
const newState = !_alwaysOnTopWindowEnabled;
|
||||
|
||||
this.props.dispatch(setWindowAlwaysOnTop(newState));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {{
|
||||
* _alwaysOnTopWindowEnabled: boolean,
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: Object) {
|
||||
return {
|
||||
_alwaysOnTopWindowEnabled: state.settings.alwaysOnTopWindowEnabled
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(AlwaysOnTopWindowToggle);
|
|
@ -3,7 +3,9 @@
|
|||
import { FieldTextStateless } from '@atlaskit/field-text';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { compose } from 'redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import config from '../../config';
|
||||
|
@ -22,6 +24,11 @@ type Props = {
|
|||
* Default Jitsi Meet Server Timeout in (redux) store.
|
||||
*/
|
||||
_serverTimeout: number;
|
||||
|
||||
/**
|
||||
* I18next translation function.
|
||||
*/
|
||||
t: Function;
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -64,6 +71,8 @@ class ServerTimeoutField extends Component<Props, State> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<Form onSubmit = { this._onServerTimeoutSubmit }>
|
||||
<FieldTextStateless
|
||||
|
@ -71,7 +80,7 @@ class ServerTimeoutField extends Component<Props, State> {
|
|||
= { 'Invalid Timeout' }
|
||||
isInvalid = { !this.state.isValid }
|
||||
isValidationHidden = { this.state.isValid }
|
||||
label = 'Server Timeout (in seconds)'
|
||||
label = { t('settings.serverTimeout') }
|
||||
onBlur = { this._onServerTimeoutSubmit }
|
||||
onChange = { this._onServerTimeoutChange }
|
||||
placeholder = { config.defaultServerTimeout }
|
||||
|
@ -138,4 +147,4 @@ function _mapStateToProps(state: Object) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(ServerTimeoutField);
|
||||
export default compose(connect(_mapStateToProps), withTranslation())(ServerTimeoutField);
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
import { FieldTextStateless } from '@atlaskit/field-text';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
import { compose } from 'redux';
|
||||
|
||||
import config from '../../config';
|
||||
import { getExternalApiURL } from '../../utils';
|
||||
|
@ -23,6 +25,11 @@ type Props = {
|
|||
* Default Jitsi Meet Server URL in (redux) store.
|
||||
*/
|
||||
_serverURL: string;
|
||||
|
||||
/**
|
||||
* I18next translation function.
|
||||
*/
|
||||
t: Function;
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -65,14 +72,15 @@ class ServerURLField extends Component<Props, State> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<Form onSubmit = { this._onServerURLSubmit }>
|
||||
<FieldTextStateless
|
||||
invalidMessage
|
||||
= { 'Invalid Server URL or external API not enabled' }
|
||||
invalidMessage = { t('settings.invalidServer') }
|
||||
isInvalid = { !this.state.isValid }
|
||||
isValidationHidden = { this.state.isValid }
|
||||
label = 'Server URL'
|
||||
label = { t('settings.serverUrl') }
|
||||
onBlur = { this._onServerURLSubmit }
|
||||
onChange = { this._onServerURLChange }
|
||||
placeholder = { config.defaultServerURL }
|
||||
|
@ -150,4 +158,4 @@ function _mapStateToProps(state: Object) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(ServerURLField);
|
||||
export default compose(connect(_mapStateToProps), withTranslation())(ServerURLField);
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// @flow
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import ToggleWithLabel from './ToggleWithLabel';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
|
||||
/**
|
||||
* The label for the toggle.
|
||||
*/
|
||||
label: String;
|
||||
|
||||
/**
|
||||
* The name of the setting.
|
||||
*/
|
||||
settingName: String;
|
||||
|
||||
/**
|
||||
* A function to produce setting change events.
|
||||
*/
|
||||
settingChangeEvent: Function;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @param {Object} ownProps - The props of the redux wrapper component.
|
||||
* @returns {Object} A props object including the current value of the setting.
|
||||
*/
|
||||
const mapStateToProps = (state, ownProps: Props) => {
|
||||
return {
|
||||
value: state.settings[ownProps.settingName],
|
||||
...ownProps
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* A component to control a single boolean redux setting.
|
||||
*
|
||||
* @param {Object} props - The props provided by mapStateToProps.
|
||||
* @returns {Object} A rendered toggle component with correct state.
|
||||
*/
|
||||
function SettingToggle(props: Object) {
|
||||
const onChange = useCallback(
|
||||
() => props.dispatch(props.settingChangeEvent(!props.value)));
|
||||
|
||||
return (
|
||||
<ToggleWithLabel
|
||||
isDefaultChecked = { props.value }
|
||||
label = { props.label }
|
||||
onChange = { onChange }
|
||||
value = { props.value } />
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(SettingToggle);
|
|
@ -7,18 +7,22 @@ import { SpotlightTarget } from '@atlaskit/onboarding';
|
|||
import Panel from '@atlaskit/panel';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
import { compose } from 'redux';
|
||||
|
||||
import { closeDrawer, DrawerContainer, Logo } from '../../navbar';
|
||||
import { Onboarding, advenaceSettingsSteps, startOnboarding } from '../../onboarding';
|
||||
import { Form, SettingsContainer, TogglesContainer } from '../styled';
|
||||
import { setEmail, setName } from '../actions';
|
||||
import {
|
||||
setEmail, setName, setWindowAlwaysOnTop,
|
||||
setStartWithAudioMuted, setStartWithVideoMuted
|
||||
} from '../actions';
|
||||
|
||||
import AlwaysOnTopWindowToggle from './AlwaysOnTopWindowToggle';
|
||||
import SettingToggle from './SettingToggle';
|
||||
import ServerURLField from './ServerURLField';
|
||||
import ServerTimeoutField from './ServerTimeoutField';
|
||||
import StartMutedToggles from './StartMutedToggles';
|
||||
|
||||
type Props = {
|
||||
|
||||
|
@ -46,6 +50,11 @@ type Props = {
|
|||
* Name of the user.
|
||||
*/
|
||||
_name: string;
|
||||
|
||||
/**
|
||||
* I18next translation function.
|
||||
*/
|
||||
t: Function;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -92,9 +101,11 @@ class SettingsDrawer extends Component<Props, *> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<AkCustomDrawer
|
||||
backIcon = { <ArrowLeft label = 'Back' /> }
|
||||
backIcon = { <ArrowLeft label = { t('settings.back') } /> }
|
||||
isOpen = { this.props.isOpen }
|
||||
onBackButton = { this._onBackButton }
|
||||
primaryIcon = { <Logo /> } >
|
||||
|
@ -104,7 +115,7 @@ class SettingsDrawer extends Component<Props, *> {
|
|||
name = 'name-setting'>
|
||||
<Form onSubmit = { this._onNameFormSubmit }>
|
||||
<FieldText
|
||||
label = 'Name'
|
||||
label = { t('settings.name') }
|
||||
onBlur = { this._onNameBlur }
|
||||
shouldFitContainer = { true }
|
||||
type = 'text'
|
||||
|
@ -115,7 +126,7 @@ class SettingsDrawer extends Component<Props, *> {
|
|||
name = 'email-setting'>
|
||||
<Form onSubmit = { this._onEmailFormSubmit }>
|
||||
<FieldText
|
||||
label = 'Email'
|
||||
label = { t('settings.email') }
|
||||
onBlur = { this._onEmailBlur }
|
||||
shouldFitContainer = { true }
|
||||
type = 'text'
|
||||
|
@ -125,11 +136,18 @@ class SettingsDrawer extends Component<Props, *> {
|
|||
<TogglesContainer>
|
||||
<SpotlightTarget
|
||||
name = 'start-muted-toggles'>
|
||||
<StartMutedToggles />
|
||||
<SettingToggle
|
||||
label = { t('settings.startWithAudioMuted') }
|
||||
settingChangeEvent = { setStartWithAudioMuted }
|
||||
settingName = 'startWithAudioMuted' />
|
||||
<SettingToggle
|
||||
label = { t('settings.startWithVideoMuted') }
|
||||
settingChangeEvent = { setStartWithVideoMuted }
|
||||
settingName = 'startWithVideoMuted' />
|
||||
</SpotlightTarget>
|
||||
</TogglesContainer>
|
||||
<Panel
|
||||
header = 'Advanced Settings'
|
||||
header = { t('settings.advancedSettings') }
|
||||
isDefaultExpanded = { this.props._isOnboardingAdvancedSettings }>
|
||||
<SpotlightTarget name = 'server-setting'>
|
||||
<ServerURLField />
|
||||
|
@ -140,7 +158,10 @@ class SettingsDrawer extends Component<Props, *> {
|
|||
<TogglesContainer>
|
||||
<SpotlightTarget
|
||||
name = 'always-on-top-window'>
|
||||
<AlwaysOnTopWindowToggle />
|
||||
<SettingToggle
|
||||
label = { t('settings.alwaysOnTopWindow') }
|
||||
settingChangeEvent = { setWindowAlwaysOnTop }
|
||||
settingName = 'alwaysOnTopWindowEnabled' />
|
||||
</SpotlightTarget>
|
||||
</TogglesContainer>
|
||||
</Panel>
|
||||
|
@ -236,4 +257,4 @@ function _mapStateToProps(state: Object) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(SettingsDrawer);
|
||||
export default compose(connect(_mapStateToProps), withTranslation())(SettingsDrawer);
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import {
|
||||
setStartWithAudioMuted,
|
||||
setStartWithVideoMuted
|
||||
} from '../actions';
|
||||
|
||||
import ToggleWithLabel from './ToggleWithLabel';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch.
|
||||
*/
|
||||
dispatch: Dispatch<*>;
|
||||
|
||||
/**
|
||||
* Start with Audio Muted value in (redux) state.
|
||||
*/
|
||||
_startWithAudioMuted: boolean;
|
||||
|
||||
/**
|
||||
* Start with Video Muted value in (redux) state.
|
||||
*/
|
||||
_startWithVideoMuted: boolean;
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* Start with Audio Muted value in (local) state.
|
||||
*/
|
||||
startWithAudioMuted: boolean;
|
||||
|
||||
/**
|
||||
* Start with Video Muted value in (local) state.
|
||||
*/
|
||||
startWithVideoMuted: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Start Muted toggles for audio and video placed in Settings Drawer.
|
||||
*/
|
||||
class StartMutedToggles extends Component<Props, State> {
|
||||
/**
|
||||
* Initializes a new {@code StartMutedToggles} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
startWithAudioMuted: false,
|
||||
startWithVideoMuted: false
|
||||
};
|
||||
|
||||
this._onAudioToggleChange = this._onAudioToggleChange.bind(this);
|
||||
this._onVideoToggleChange = this._onVideoToggleChange.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* This updates the startWithAudioMuted and startWithVideoMuted in (local)
|
||||
* state when there is a change in redux store.
|
||||
*
|
||||
* @param {Props} props - New props of the component.
|
||||
* @returns {State} - New state of the component.
|
||||
*/
|
||||
static getDerivedStateFromProps(props) {
|
||||
return {
|
||||
startWithAudioMuted: props._startWithAudioMuted,
|
||||
startWithVideoMuted: props._startWithVideoMuted
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Render function of component.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<ToggleWithLabel
|
||||
isDefaultChecked = { this.props._startWithAudioMuted }
|
||||
label = 'Start with Audio muted'
|
||||
onChange = { this._onAudioToggleChange }
|
||||
value = { this.state.startWithAudioMuted } />
|
||||
<ToggleWithLabel
|
||||
isDefaultChecked = { this.props._startWithVideoMuted }
|
||||
label = 'Start with Video muted'
|
||||
onChange = { this._onVideoToggleChange }
|
||||
value = { this.state.startWithVideoMuted } />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
_onAudioToggleChange: (*) => void;
|
||||
|
||||
/**
|
||||
* Toggles startWithAudioMuted.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onAudioToggleChange() {
|
||||
const { startWithAudioMuted } = this.state;
|
||||
|
||||
this.props.dispatch(setStartWithAudioMuted(!startWithAudioMuted));
|
||||
}
|
||||
|
||||
_onVideoToggleChange: (*) => void;
|
||||
|
||||
/**
|
||||
* Toggles startWithVideoMuted.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onVideoToggleChange() {
|
||||
const { startWithVideoMuted } = this.state;
|
||||
|
||||
this.props.dispatch(setStartWithVideoMuted(!startWithVideoMuted));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {{
|
||||
* _startWithAudioMuted: boolean,
|
||||
* _startWithVideoMuted: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: Object) {
|
||||
return {
|
||||
_startWithAudioMuted: state.settings.startWithAudioMuted,
|
||||
_startWithVideoMuted: state.settings.startWithVideoMuted
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(StartMutedToggles);
|
|
@ -8,6 +8,8 @@ import { AtlasKitThemeProvider } from '@atlaskit/theme';
|
|||
|
||||
import { generateRoomWithoutSeparator } from 'js-utils/random';
|
||||
import React, { Component } from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { compose } from 'redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { push } from 'react-router-redux';
|
||||
|
@ -19,7 +21,6 @@ import { createConferenceObjectFromURL } from '../../utils';
|
|||
|
||||
import { Body, FieldWrapper, Form, Header, Label, Wrapper } from '../styled';
|
||||
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
|
@ -31,6 +32,11 @@ type Props = {
|
|||
* React Router location object.
|
||||
*/
|
||||
location: Object;
|
||||
|
||||
/**
|
||||
* I18next translate function.
|
||||
*/
|
||||
t: Function;
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -252,12 +258,13 @@ class Welcome extends Component<Props, State> {
|
|||
_renderHeader() {
|
||||
const locationState = this.props.location.state;
|
||||
const locationError = locationState && locationState.error;
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<Header>
|
||||
<SpotlightTarget name = 'conference-url'>
|
||||
<Form onSubmit = { this._onFormSubmit }>
|
||||
<Label>{ 'Enter a name for your conference or a Jitsi URL' } </Label>
|
||||
<Label>{ t('enterConferenceNameOrUrl') } </Label>
|
||||
<FieldWrapper>
|
||||
<FieldTextStateless
|
||||
autoFocus = { true }
|
||||
|
@ -272,7 +279,7 @@ class Welcome extends Component<Props, State> {
|
|||
appearance = 'primary'
|
||||
onClick = { this._onJoin }
|
||||
type = 'button'>
|
||||
GO
|
||||
{ t('go') }
|
||||
</Button>
|
||||
</FieldWrapper>
|
||||
</Form>
|
||||
|
@ -306,4 +313,4 @@ class Welcome extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
export default connect()(Welcome);
|
||||
export default compose(connect(), withTranslation())(Welcome);
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import moment from 'moment';
|
||||
|
||||
const languages = {
|
||||
en: { translation: require('./lang/en.json') }
|
||||
};
|
||||
|
||||
const detectedLocale = window.jitsiNodeAPI.getLocale();
|
||||
|
||||
i18n
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources: languages,
|
||||
lng: detectedLocale,
|
||||
fallbackLng: 'en',
|
||||
interpolation: {
|
||||
escapeValue: false // not needed for react as it escapes by default
|
||||
}
|
||||
});
|
||||
|
||||
moment.locale(detectedLocale);
|
||||
|
||||
export default i18n;
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"enterConferenceNameOrUrl": "Enter a name for your conference or a Jitsi URL",
|
||||
"go": "GO",
|
||||
"help": "Help",
|
||||
"termsLink": "Terms",
|
||||
"privacyLink": "Privacy",
|
||||
"sendFeedbackLink": "Send Feedback",
|
||||
"aboutLink": "About",
|
||||
"sourceLink": "Source Code",
|
||||
"versionLabel": "Version: {{version}}",
|
||||
"onboarding": {
|
||||
"startTour": "Start Tour",
|
||||
"skip": "Skip",
|
||||
"welcome": "Welcome to {{appName}}",
|
||||
"letUsShowYouAround": "Let us show you around!",
|
||||
"next": "Next",
|
||||
"conferenceUrl": "Enter the name (or full URL) of the room you want to join. You may make a name up, just let others know so they enter the same name.",
|
||||
"settingsDrawerButton": "Click here to open the settings drawer.",
|
||||
"nameSetting": "This will be your display name, others will see you with this name.",
|
||||
"emailSetting": "The email you enter here will be part of your user profile.",
|
||||
"startMutedToggles": "You can toggle if you want to start with your audio or video muted here. This will be applied to all conferences.",
|
||||
"serverSetting": "This will be the server where your conferences will take place. You can use your own, but you don't need to!",
|
||||
"serverTimeout": "Timeout to join a meeting, if the meeting hasn't been joined before the timeout hits, it's cancelled.",
|
||||
"alwaysOnTop": "You can toggle whether you want to enable the \"always-on-top\" window, which is displayed when the main window loses focus. This will be applied to all conferences."
|
||||
},
|
||||
"settings": {
|
||||
"back": "Back",
|
||||
"name": "Name",
|
||||
"email": "Email",
|
||||
"advancedSettings": "Advanced Settings",
|
||||
"alwaysOnTopWindow": "Always on Top Window",
|
||||
"startWithAudioMuted": "Start with Audio muted",
|
||||
"startWithVideoMuted": "Start with Video muted",
|
||||
"invalidServer": "Invalid Server URL or external API not enabled",
|
||||
"serverUrl": "Server URL",
|
||||
"serverTimeout": "Server Timeout (in seconds)"
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<style>
|
||||
body, html {
|
||||
overflow: hidden;
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
*/
|
||||
import '@atlaskit/css-reset';
|
||||
|
||||
import Spinner from '@atlaskit/spinner';
|
||||
import { SpotlightManager } from '@atlaskit/onboarding';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import React, { Component, Suspense } from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
|
@ -15,6 +16,8 @@ import { PersistGate } from 'redux-persist/integration/react';
|
|||
import { App } from './features/app';
|
||||
import { persistor, store } from './features/redux';
|
||||
|
||||
import './i18n';
|
||||
|
||||
/**
|
||||
* Component encapsulating App component with redux store using provider.
|
||||
*/
|
||||
|
@ -31,7 +34,9 @@ class Root extends Component<*> {
|
|||
loading = { null }
|
||||
persistor = { persistor }>
|
||||
<SpotlightManager>
|
||||
<Suspense fallback = { <Spinner /> } >
|
||||
<App />
|
||||
</Suspense>
|
||||
</SpotlightManager>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const createElectronStorage = require('redux-persist-electron-storage');
|
||||
const { ipcRenderer, shell } = require('electron');
|
||||
const { ipcRenderer, shell, remote } = require('electron');
|
||||
const os = require('os');
|
||||
const url = require('url');
|
||||
|
||||
|
@ -35,6 +35,7 @@ window.jitsiNodeAPI = {
|
|||
openExternalLink,
|
||||
jitsiMeetElectronUtils,
|
||||
shellOpenExternal: shell.openExternal,
|
||||
getLocale: remote.app.getLocale,
|
||||
ipc: {
|
||||
on: (channel, listener) => {
|
||||
if (!whitelistedIpcChannels.includes(channel)) {
|
||||
|
|
|
@ -9624,6 +9624,14 @@
|
|||
"terser": "^4.6.3"
|
||||
}
|
||||
},
|
||||
"html-parse-stringify2": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz",
|
||||
"integrity": "sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o=",
|
||||
"requires": {
|
||||
"void-elements": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"html-webpack-plugin": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.4.tgz",
|
||||
|
@ -9692,6 +9700,14 @@
|
|||
"integrity": "sha1-pls0RZrWNnrbs3B6gqPJ+RYWcDA=",
|
||||
"dev": true
|
||||
},
|
||||
"i18next": {
|
||||
"version": "19.4.5",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-19.4.5.tgz",
|
||||
"integrity": "sha512-aLvSsURoupi3x9IndmV6+m3IGhzLzhYv7Gw+//K3ovdliyGcFRV0I1MuddI0Bk/zR7BG1U+kJOjeHFUcUIdEgg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1"
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
|
@ -12170,14 +12186,14 @@
|
|||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "16.6.3",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.6.3.tgz",
|
||||
"integrity": "sha512-zCvmH2vbEolgKxtqXL2wmGCUxUyNheYn/C+PD1YAjfxHC54+MhdruyhO7QieQrYsYeTxrn93PM2y0jRH1zEExw==",
|
||||
"version": "16.8.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.8.0.tgz",
|
||||
"integrity": "sha512-g+nikW2D48kqgWSPwNo0NH9tIGG3DsQFlrtrQ1kj6W77z5ahyIHG0w8kPpz4Sdj6gyLnz0lEd/xsjOoGge2MYQ==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.11.2"
|
||||
"scheduler": "^0.13.0"
|
||||
}
|
||||
},
|
||||
"react-addons-text-content": {
|
||||
|
@ -12203,14 +12219,14 @@
|
|||
}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "16.6.3",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.6.3.tgz",
|
||||
"integrity": "sha512-8ugJWRCWLGXy+7PmNh8WJz3g1TaTUt1XyoIcFN+x0Zbkoz+KKdUyx1AQLYJdbFXjuF41Nmjn5+j//rxvhFjgSQ==",
|
||||
"version": "16.8.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.0.tgz",
|
||||
"integrity": "sha512-dBzoAGYZpW9Yggp+CzBPC7q1HmWSeRc93DWrwbskmG1eHJWznZB/p0l/Sm+69leIGUS91AXPB/qB3WcPnKx8Sw==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.11.2"
|
||||
"scheduler": "^0.13.0"
|
||||
}
|
||||
},
|
||||
"react-focus-lock": {
|
||||
|
@ -12233,6 +12249,15 @@
|
|||
"prop-types": "^15.6.1"
|
||||
}
|
||||
},
|
||||
"react-i18next": {
|
||||
"version": "11.5.1",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.5.1.tgz",
|
||||
"integrity": "sha512-2VSx+dClvmCJTgrw9Nrof4EkggMlzo4s/YZPEUAdI7gU2HTksapkGtFPARzpeTk+3k1zY+Xl6pLHc0LYvsRWAQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"html-parse-stringify2": "2.0.1"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.10.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.10.2.tgz",
|
||||
|
@ -12863,9 +12888,9 @@
|
|||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||
},
|
||||
"scheduler": {
|
||||
"version": "0.11.3",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.11.3.tgz",
|
||||
"integrity": "sha512-i9X9VRRVZDd3xZw10NY5Z2cVMbdYg6gqFecfj79USv1CFN+YrJ3gIPRKf1qlY+Sxly4djoKdfx1T+m9dnRB8kQ==",
|
||||
"version": "0.13.6",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz",
|
||||
"integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
|
@ -14382,6 +14407,11 @@
|
|||
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"void-elements": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
|
||||
"integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w="
|
||||
},
|
||||
"warning": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
|
||||
|
|
|
@ -116,12 +116,14 @@
|
|||
"electron-updater": "4.2.5",
|
||||
"electron-window-state": "5.0.3",
|
||||
"history": "4.10.1",
|
||||
"i18next": "19.4.5",
|
||||
"jitsi-meet-electron-utils": "github:jitsi/jitsi-meet-electron-utils#v2.0.7",
|
||||
"js-utils": "github:jitsi/js-utils#cf11996bd866fdb47326c59a5d3bc24be17282d4",
|
||||
"moment": "2.23.0",
|
||||
"mousetrap": "1.6.2",
|
||||
"react": "16.6.3",
|
||||
"react-dom": "16.6.3",
|
||||
"react": "16.8.0",
|
||||
"react-dom": "16.8.0",
|
||||
"react-i18next": "11.5.1",
|
||||
"react-redux": "5.1.1",
|
||||
"react-router-redux": "5.0.0-alpha.9",
|
||||
"redux": "4.0.1",
|
||||
|
|
Loading…
Reference in New Issue