Added internationalization support, and translated to nb-NO.

This commit is contained in:
Håvar Aambø Fosstveit
2019-11-06 23:11:42 +01:00
parent 23e02d4865
commit c12057e53c
23 changed files with 1214 additions and 375 deletions
@@ -4,6 +4,7 @@ import { withStyles } from '@material-ui/core/styles';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { withRoomContext } from '../../../RoomContext';
import { useIntl } from 'react-intl';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ListItemIcon from '@material-ui/core/ListItemIcon';
@@ -85,6 +86,8 @@ const ListLobbyPeer = (props) =>
classes
} = props;
const intl = useIntl();
const picture = peer.picture || EmptyAvatar;
return (
@@ -100,7 +103,12 @@ const ListLobbyPeer = (props) =>
<ListItemText
primary={peer.displayName}
/>
<Tooltip title='Click to let them in'>
<Tooltip
title={intl.formatMessage({
id : 'tooltip.admitFromLobby',
defaultMessage : 'Click to let them in'
})}
>
<ListItemIcon
className={classnames(classes.button, 'promote', {
disabled : peer.promotionInProgress
@@ -8,6 +8,7 @@ import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../../RoomContext';
import * as roomActions from '../../../actions/roomActions';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogActions from '@material-ui/core/DialogActions';
@@ -75,7 +76,12 @@ const LockDialog = ({
paper : classes.dialogPaper
}}
>
<DialogTitle id='form-dialog-title'>Lobby administration</DialogTitle>
<DialogTitle id='form-dialog-title'>
<FormattedMessage
id='room.lobbyAdministration'
defaultMessage='Lobby administration'
/>
</DialogTitle>
{/*
<FormControl component='fieldset' className={classes.formControl}>
<FormLabel component='legend'>Room lock</FormLabel>
@@ -129,7 +135,10 @@ const LockDialog = ({
dense
subheader={
<ListSubheader component='div'>
Participants in Lobby
<FormattedMessage
id='room.peersInLobby'
defaultMessage='Participants in Lobby'
/>
</ListSubheader>
}
>
@@ -143,13 +152,19 @@ const LockDialog = ({
:
<DialogContent>
<DialogContentText gutterBottom>
There are currently no one in the lobby.
<FormattedMessage
id='room.lobbyEmpty'
defaultMessage='There are currently no one in the lobby'
/>
</DialogContentText>
</DialogContent>
}
<DialogActions>
<Button onClick={() => handleCloseLockDialog({ lockDialogOpen: false })} color='primary'>
Close
<FormattedMessage
id='label.close'
defaultMessage='Close'
/>
</Button>
</DialogActions>
</Dialog>
+14 -2
View File
@@ -3,6 +3,7 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { withStyles } from '@material-ui/core/styles';
import { FormattedMessage } from 'react-intl';
import * as toolareaActions from '../../actions/toolareaActions';
import BuddyImage from '../../images/buddy.svg';
@@ -95,8 +96,19 @@ class HiddenPeers extends React.PureComponent
className={classnames(classes.root, this.state.className)}
onClick={() => openUsersTab()}
>
<p>+{hiddenPeersCount} <br /> participant
{(hiddenPeersCount > 1) && 's'}
<p>
+{hiddenPeersCount} <br />
<FormattedMessage
id='room.hiddenPeers'
defaultMessage={
`{hiddenPeersCount, plural,
one {participant}
other {participants}}`
}
values={{
hiddenPeersCount
}}
/>
</p>
</div>
);
+70 -18
View File
@@ -7,6 +7,7 @@ import useMediaQuery from '@material-ui/core/useMediaQuery';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import * as appPropTypes from '../appPropTypes';
import { useIntl, FormattedMessage } from 'react-intl';
import VideoView from '../VideoContainers/VideoView';
import Volume from './Volume';
import Fab from '@material-ui/core/Fab';
@@ -96,6 +97,8 @@ const Me = (props) =>
{
const [ hover, setHover ] = useState(false);
const intl = useIntl();
let touchTimeout = null;
const {
@@ -133,22 +136,34 @@ const Me = (props) =>
if (!me.canSendMic)
{
micState = 'unsupported';
micTip = 'Audio unsupported';
micTip = intl.formatMessage({
id : 'device.audioUnsupported',
defaultMessage : 'Audio unsupported'
});
}
else if (!micProducer)
{
micState = 'off';
micTip = 'Activate audio';
micTip = intl.formatMessage({
id : 'device.activateAudio',
defaultMessage : 'Activate audio'
});
}
else if (!micProducer.locallyPaused && !micProducer.remotelyPaused)
{
micState = 'on';
micTip = 'Mute audio';
micTip = intl.formatMessage({
id : 'device.muteAudio',
defaultMessage : 'Mute audio'
});
}
else
{
micState = 'muted';
micTip = 'Unmute audio';
micTip = intl.formatMessage({
id : 'device.unMuteAudio',
defaultMessage : 'Unmute audio'
});
}
let webcamState;
@@ -158,17 +173,26 @@ const Me = (props) =>
if (!me.canSendWebcam)
{
webcamState = 'unsupported';
webcamTip = 'Video unsupported';
webcamTip = intl.formatMessage({
id : 'device.videoUnsupported',
defaultMessage : 'Video unsupported'
});
}
else if (webcamProducer)
{
webcamState = 'on';
webcamTip = 'Stop video';
webcamTip = intl.formatMessage({
id : 'device.stopVideo',
defaultMessage : 'Stop video'
});
}
else
{
webcamState = 'off';
webcamTip = 'Start video';
webcamTip = intl.formatMessage({
id : 'device.startVideo',
defaultMessage : 'Start video'
});
}
let screenState;
@@ -178,17 +202,26 @@ const Me = (props) =>
if (!me.canShareScreen)
{
screenState = 'unsupported';
screenTip = 'Screen sharing not supported';
screenTip = intl.formatMessage({
id : 'device.screenSharingUnsupported',
defaultMessage : 'Screen sharing not supported'
});
}
else if (screenProducer)
{
screenState = 'on';
screenTip = 'Stop screen sharing';
screenTip = intl.formatMessage({
id : 'device.stopScreenSharing',
defaultMessage : 'Stop screen sharing'
});
}
else
{
screenState = 'off';
screenTip = 'Start screen sharing';
screenTip = intl.formatMessage({
id : 'device.startScreenSharing',
defaultMessage : 'Start screen sharing'
});
}
const spacingStyle =
@@ -253,11 +286,19 @@ const Me = (props) =>
}, 2000);
}}
>
<p>ME</p>
<Tooltip title={micTip} placement={smallScreen ? 'top' : 'right'}>
<p>
<FormattedMessage
id='room.me'
defaultMessage='ME'
/>
</p>
<Tooltip title={micTip} placement={smallScreen ? 'top' : 'left'}>
<div>
<Fab
aria-label='Mute mic'
aria-label={intl.formatMessage({
id : 'device.muteAudio',
defaultMessage : 'Mute audio'
})}
className={classes.fab}
disabled={!me.canSendMic || me.audioInProgress}
color={micState === 'on' ? 'default' : 'secondary'}
@@ -280,10 +321,13 @@ const Me = (props) =>
</Fab>
</div>
</Tooltip>
<Tooltip title={webcamTip} placement={smallScreen ? 'top' : 'right'}>
<Tooltip title={webcamTip} placement={smallScreen ? 'top' : 'left'}>
<div>
<Fab
aria-label='Mute video'
aria-label={intl.formatMessage({
id : 'device.startVideo',
defaultMessage : 'Start video'
})}
className={classes.fab}
disabled={!me.canSendWebcam || me.webcamInProgress}
color={webcamState === 'on' ? 'default' : 'secondary'}
@@ -303,10 +347,13 @@ const Me = (props) =>
</Fab>
</div>
</Tooltip>
<Tooltip title={screenTip} placement={smallScreen ? 'top' : 'right'}>
<Tooltip title={screenTip} placement={smallScreen ? 'top' : 'left'}>
<div>
<Fab
aria-label='Share screen'
aria-label={intl.formatMessage({
id : 'device.startScreenSharing',
defaultMessage : 'Start screen sharing'
})}
className={classes.fab}
disabled={!me.canShareScreen || me.screenShareInProgress}
color={screenState === 'on' ? 'primary' : 'default'}
@@ -410,7 +457,12 @@ const Me = (props) =>
}, 2000);
}}
>
<p>ME</p>
<p>
<FormattedMessage
id='room.me'
defaultMessage='ME'
/>
</p>
</div>
<VideoView
+149 -70
View File
@@ -8,7 +8,9 @@ import { withRoomContext } from '../../RoomContext';
import { withStyles } from '@material-ui/core/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import * as roomActions from '../../actions/roomActions';
import { useIntl, FormattedMessage } from 'react-intl';
import VideoView from '../VideoContainers/VideoView';
import Tooltip from '@material-ui/core/Tooltip';
import Fab from '@material-ui/core/Fab';
import MicIcon from '@material-ui/icons/Mic';
import MicOffIcon from '@material-ui/icons/MicOff';
@@ -103,6 +105,8 @@ const Peer = (props) =>
{
const [ hover, setHover ] = useState(false);
const intl = useIntl();
let touchTimeout = null;
const {
@@ -194,7 +198,12 @@ const Peer = (props) =>
<div className={classnames(classes.viewContainer)}>
{ !videoVisible &&
<div className={classes.videoInfo}>
<p>this video is paused</p>
<p>
<FormattedMessage
id='room.videoPaused'
defaultMessage='This video is paused'
/>
</p>
</div>
}
@@ -220,56 +229,95 @@ const Peer = (props) =>
}, 2000);
}}
>
<Fab
aria-label='Mute mic'
className={classes.fab}
disabled={!micConsumer}
color={micEnabled ? 'default' : 'secondary'}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
micEnabled ?
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
}}
<Tooltip
title={intl.formatMessage({
id : 'device.muteAudio',
defaultMessage : 'Mute audio'
})}
placement={smallScreen ? 'top' : 'left'}
>
{ micEnabled ?
<MicIcon />
:
<MicOffIcon />
}
</Fab>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'device.muteAudio',
defaultMessage : 'Mute audio'
})}
className={classes.fab}
disabled={!micConsumer}
color={micEnabled ? 'default' : 'secondary'}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
micEnabled ?
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
}}
>
{ micEnabled ?
<MicIcon />
:
<MicOffIcon />
}
</Fab>
</div>
</Tooltip>
{ !smallScreen &&
<Fab
aria-label='New window'
className={classes.fab}
disabled={
!videoVisible ||
(windowConsumer === webcamConsumer.id)
}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
toggleConsumerWindow(webcamConsumer);
}}
<Tooltip
title={intl.formatMessage({
id : 'label.newWindow',
defaultMessage : 'New window'
})}
placement={smallScreen ? 'top' : 'left'}
>
<NewWindowIcon />
</Fab>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'label.newWindow',
defaultMessage : 'New window'
})}
className={classes.fab}
disabled={
!videoVisible ||
(windowConsumer === webcamConsumer.id)
}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
toggleConsumerWindow(webcamConsumer);
}}
>
<NewWindowIcon />
</Fab>
</div>
</Tooltip>
}
<Fab
aria-label='Fullscreen'
className={classes.fab}
disabled={!videoVisible}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
toggleConsumerFullscreen(webcamConsumer);
}}
<Tooltip
title={intl.formatMessage({
id : 'label.fullscreen',
defaultMessage : 'Fullscreen'
})}
placement={smallScreen ? 'top' : 'left'}
>
<FullScreenIcon />
</Fab>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'label.fullscreen',
defaultMessage : 'Fullscreen'
})}
className={classes.fab}
disabled={!videoVisible}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
toggleConsumerFullscreen(webcamConsumer);
}}
>
<FullScreenIcon />
</Fab>
</div>
</Tooltip>
</div>
<VideoView
@@ -314,7 +362,12 @@ const Peer = (props) =>
>
{ !screenVisible &&
<div className={classes.videoInfo}>
<p>this video is paused</p>
<p>
<FormattedMessage
id='room.videoPaused'
defaultMessage='This video is paused'
/>
</p>
</div>
}
@@ -344,35 +397,61 @@ const Peer = (props) =>
}}
>
{ !smallScreen &&
<Fab
aria-label='New window'
className={classes.fab}
disabled={
!screenVisible ||
(windowConsumer === screenConsumer.id)
}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
toggleConsumerWindow(screenConsumer);
}}
<Tooltip
title={intl.formatMessage({
id : 'label.newWindow',
defaultMessage : 'New window'
})}
placement={smallScreen ? 'top' : 'left'}
>
<NewWindowIcon />
</Fab>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'label.newWindow',
defaultMessage : 'New window'
})}
className={classes.fab}
disabled={
!screenVisible ||
(windowConsumer === screenConsumer.id)
}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
toggleConsumerWindow(screenConsumer);
}}
>
<NewWindowIcon />
</Fab>
</div>
</Tooltip>
}
<Fab
aria-label='Fullscreen'
className={classes.fab}
disabled={!screenVisible}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
toggleConsumerFullscreen(screenConsumer);
}}
<Tooltip
title={intl.formatMessage({
id : 'label.fullscreen',
defaultMessage : 'Fullscreen'
})}
placement={smallScreen ? 'top' : 'left'}
>
<FullScreenIcon />
</Fab>
<div>
<Fab
aria-label={intl.formatMessage({
id : 'label.fullscreen',
defaultMessage : 'Fullscreen'
})}
className={classes.fab}
disabled={!screenVisible}
size={smallButtons ? 'small' : 'large'}
onClick={() =>
{
toggleConsumerFullscreen(screenConsumer);
}}
>
<FullScreenIcon />
</Fab>
</div>
</Tooltip>
</div>
<VideoView
advancedMode={advancedMode}
+13 -2
View File
@@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import * as appPropTypes from '../appPropTypes';
import { withStyles } from '@material-ui/core/styles';
import { FormattedMessage } from 'react-intl';
import VideoView from '../VideoContainers/VideoView';
import Volume from './Volume';
@@ -119,7 +120,12 @@ const SpeakerPeer = (props) =>
<div className={classnames(classes.viewContainer)} style={style}>
{ !videoVisible &&
<div className={classes.videoInfo}>
<p>this video is paused</p>
<p>
<FormattedMessage
id='room.videoPaused'
defaultMessage='This video is paused'
/>
</p>
</div>
}
@@ -145,7 +151,12 @@ const SpeakerPeer = (props) =>
>
{ !screenVisible &&
<div className={classes.videoInfo} style={style}>
<p>this video is paused</p>
<p>
<FormattedMessage
id='room.videoPaused'
defaultMessage='This video is paused'
/>
</p>
</div>
}
+83 -13
View File
@@ -9,6 +9,7 @@ import { withRoomContext } from '../../RoomContext';
import { withStyles } from '@material-ui/core/styles';
import * as roomActions from '../../actions/roomActions';
import * as toolareaActions from '../../actions/toolareaActions';
import { useIntl, FormattedMessage } from 'react-intl';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
@@ -109,6 +110,8 @@ const PulsingBadge = withStyles((theme) =>
const TopBar = (props) =>
{
const intl = useIntl();
const {
roomClient,
room,
@@ -126,6 +129,39 @@ const TopBar = (props) =>
classes
} = props;
const lockTooltip = room.locked ?
intl.formatMessage({
id : 'tooltip.unLockRoom',
defaultMessage : 'Unlock room'
})
:
intl.formatMessage({
id : 'tooltip.lockRoom',
defaultMessage : 'Lock room'
});
const fullscreenTooltip = fullscreen ?
intl.formatMessage({
id : 'tooltip.leaveFullscreen',
defaultMessage : 'Leave fullscreen'
})
:
intl.formatMessage({
id : 'tooltip.enterFullscreen',
defaultMessage : 'Enter fullscreen'
});
const loginTooltip = loggedIn ?
intl.formatMessage({
id : 'tooltip.logout',
defaultMessage : 'Log out'
})
:
intl.formatMessage({
id : 'tooltip.login',
defaultMessage : 'Log in'
});
return (
<AppBar
position='fixed'
@@ -138,7 +174,10 @@ const TopBar = (props) =>
>
<IconButton
color='inherit'
aria-label='Open drawer'
aria-label={intl.formatMessage({
id : 'label.openDrawer',
defaultMessage : 'Open drawer'
})}
onClick={() => toggleToolArea()}
className={classes.menuButton}
>
@@ -156,9 +195,12 @@ const TopBar = (props) =>
</Typography>
<div className={classes.grow} />
<div className={classes.actionButtons}>
<Tooltip title={`${room.locked ? 'Unlock' : 'Lock'} room`}>
<Tooltip title={lockTooltip}>
<IconButton
aria-label='Lock room'
aria-label={intl.formatMessage({
id : 'tooltip.lockRoom',
defaultMessage : 'Lock room'
})}
className={classes.actionButton}
color='inherit'
onClick={() =>
@@ -181,9 +223,17 @@ const TopBar = (props) =>
</IconButton>
</Tooltip>
{ lobbyPeers.length > 0 &&
<Tooltip title='Show lobby'>
<Tooltip
title={intl.formatMessage({
id : 'tooltip.lobby',
defaultMessage : 'Show lobby'
})}
>
<IconButton
aria-label='Lobby'
aria-label={intl.formatMessage({
id : 'tooltip.lobby',
defaultMessage : 'Show lobby'
})}
color='inherit'
onClick={() => setLockDialogOpen(!room.lockDialogOpen)}
>
@@ -197,9 +247,12 @@ const TopBar = (props) =>
</Tooltip>
}
{ fullscreenEnabled &&
<Tooltip title={`${fullscreen ? 'Leave' : 'Enter'} fullscreen`}>
<Tooltip title={fullscreenTooltip}>
<IconButton
aria-label='Fullscreen'
aria-label={intl.formatMessage({
id : 'tooltip.enterFullscreen',
defaultMessage : 'Enter fullscreen'
})}
className={classes.actionButton}
color='inherit'
onClick={onFullscreen}
@@ -212,9 +265,17 @@ const TopBar = (props) =>
</IconButton>
</Tooltip>
}
<Tooltip title='Show settings'>
<Tooltip
title={intl.formatMessage({
id : 'tooltip.settings',
defaultMessage : 'Show settings'
})}
>
<IconButton
aria-label='Settings'
aria-label={intl.formatMessage({
id : 'tooltip.settings',
defaultMessage : 'Show settings'
})}
className={classes.actionButton}
color='inherit'
onClick={() => setSettingsOpen(!room.settingsOpen)}
@@ -223,9 +284,12 @@ const TopBar = (props) =>
</IconButton>
</Tooltip>
{ loginEnabled &&
<Tooltip title={`Log ${loggedIn ? 'out' : 'in'}`}>
<Tooltip title={loginTooltip}>
<IconButton
aria-label='Account'
aria-label={intl.formatMessage({
id : 'tooltip.login',
defaultMessage : 'Log in'
})}
className={classes.actionButton}
color='inherit'
onClick={() =>
@@ -242,13 +306,19 @@ const TopBar = (props) =>
</Tooltip>
}
<Button
aria-label='Leave meeting'
aria-label={intl.formatMessage({
id : 'label.leave',
defaultMessage : 'Leave'
})}
className={classes.actionButton}
variant='contained'
color='secondary'
onClick={() => roomClient.close()}
>
Leave
<FormattedMessage
id='label.leave'
defaultMessage='Leave'
/>
</Button>
</div>
</Toolbar>
+58 -13
View File
@@ -4,6 +4,7 @@ import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../RoomContext';
import * as settingsActions from '../actions/settingsActions';
import PropTypes from 'prop-types';
import { useIntl, FormattedMessage } from 'react-intl';
import Dialog from '@material-ui/core/Dialog';
import DialogContentText from '@material-ui/core/DialogContentText';
import IconButton from '@material-ui/core/IconButton';
@@ -87,6 +88,8 @@ const DialogTitle = withStyles(styles)((props) =>
{
const [ open, setOpen ] = useState(false);
const intl = useIntl();
useEffect(() =>
{
const openTimer = setTimeout(() => setOpen(true), 1000);
@@ -120,7 +123,10 @@ const DialogTitle = withStyles(styles)((props) =>
onClose={handleTooltipClose}
onOpen={handleTooltipOpen}
open={open}
title='Click to log in'
title={intl.formatMessage({
id : 'tooltip.login',
defaultMessage : 'Click to log in'
})}
placement='left'
>
<IconButton
@@ -167,6 +173,8 @@ const JoinDialog = ({
classes
}) =>
{
const intl = useIntl();
const handleKeyDown = (event) =>
{
const { key } = event;
@@ -207,21 +215,38 @@ const JoinDialog = ({
</DialogTitle>
<DialogContent>
<DialogContentText gutterBottom>
You are about to join a meeting.
<FormattedMessage
id='room.aboutToJoin'
defaultMessage='You are about to join a meeting'
/>
</DialogContentText>
<DialogContentText variant='h6' gutterBottom align='center'>
Room ID: { room.name }
<FormattedMessage
id='room.roomId'
defaultMessage='Room ID: {roomName}'
values={{
roomName : room.name
}}
/>
</DialogContentText>
<DialogContentText gutterBottom>
Set your name for participation,
and choose how you want to join:
<FormattedMessage
id='room.setYourName'
defaultMessage={
`Set your name for participation,
and choose how you want to join:`
}
/>
</DialogContentText>
<TextField
id='displayname'
label='Your name'
label={intl.formatMessage({
id : 'label.yourName',
defaultMessage : 'Your name'
})}
value={displayName}
variant='outlined'
margin='normal'
@@ -255,7 +280,10 @@ const JoinDialog = ({
variant='contained'
color='secondary'
>
Audio only
<FormattedMessage
id='room.audioOnly'
defaultMessage='Audio only'
/>
</Button>
<Button
onClick={() =>
@@ -265,7 +293,10 @@ const JoinDialog = ({
variant='contained'
color='secondary'
>
Audio and Video
<FormattedMessage
id='room.audioVideo'
defaultMessage='Audio and Video'
/>
</Button>
</DialogActions>
:
@@ -276,23 +307,37 @@ const JoinDialog = ({
variant='h6'
align='center'
>
Ok, you are ready
<FormattedMessage
id='room.youAreReady'
defaultMessage='Ok, you are ready'
/>
</DialogContentText>
{ room.signInRequired ?
<DialogContentText gutterBottom>
The room is empty!
You can Log In to start the meeting or wait until the host joins.
<FormattedMessage
id='room.emptyRequireLogin'
defaultMessage={
`The room is empty! You can Log In to start
the meeting or wait until the host joins`
}
/>
</DialogContentText>
:
<DialogContentText gutterBottom>
The room is locked - hang on until somebody lets you in ...
<FormattedMessage
id='room.locketWait'
defaultMessage='The room is locked - hang on until somebody lets you in ...'
/>
</DialogContentText>
}
</DialogContent>
}
<CookieConsent>
This website uses cookies to enhance the user experience.
<FormattedMessage
id='room.cookieConsent'
defaultMessage='This website uses cookies to enhance the user experience'
/>
</CookieConsent>
</Dialog>
</div>
@@ -1,8 +1,9 @@
import React from 'react';
import React, { useState } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../../RoomContext';
import { useIntl } from 'react-intl';
import Paper from '@material-ui/core/Paper';
import InputBase from '@material-ui/core/InputBase';
import IconButton from '@material-ui/core/IconButton';
@@ -28,19 +29,13 @@ const styles = (theme) =>
}
});
class ChatInput extends React.PureComponent
const ChatInput = (props) =>
{
constructor(props)
{
super(props);
const [ message, setMessage ] = useState('');
this.state =
{
message : ''
};
}
const intl = useIntl();
createNewMessage = (text, sender, name, picture) =>
const createNewMessage = (text, sender, name, picture) =>
({
type : 'message',
text,
@@ -50,67 +45,67 @@ class ChatInput extends React.PureComponent
picture
});
handleChange = (e) =>
const handleChange = (e) =>
{
this.setState({ message: e.target.value });
}
setMessage(e.target.value);
};
render()
{
const {
roomClient,
displayName,
picture,
classes
} = this.props;
return (
<Paper className={classes.root}>
<InputBase
className={classes.input}
placeholder='Enter chat message...'
value={this.state.message || ''}
onChange={this.handleChange}
onKeyPress={(ev) =>
const {
roomClient,
displayName,
picture,
classes
} = props;
return (
<Paper className={classes.root}>
<InputBase
className={classes.input}
placeholder={intl.formatMessage({
id : 'label.chatInput',
defaultMessage : 'Enter chat message...'
})}
value={message || ''}
onChange={handleChange}
onKeyPress={(ev) =>
{
if (ev.key === 'Enter')
{
if (ev.key === 'Enter')
ev.preventDefault();
if (message && message !== '')
{
ev.preventDefault();
const sendMessage = createNewMessage(message, 'response', displayName, picture);
if (this.state.message && this.state.message !== '')
{
const message = this.createNewMessage(this.state.message, 'response', displayName, picture);
roomClient.sendChatMessage(sendMessage);
roomClient.sendChatMessage(message);
this.setState({ message: '' });
}
setMessage('');
}
}}
autoFocus
/>
<IconButton
color='primary'
className={classes.iconButton}
aria-label='Send'
onClick={() =>
}
}}
autoFocus
/>
<IconButton
color='primary'
className={classes.iconButton}
aria-label='Send'
onClick={() =>
{
if (message && message !== '')
{
if (this.state.message && this.state.message !== '')
{
const message = this.createNewMessage(this.state.message, 'response', displayName, picture);
const sendMessage = this.createNewMessage(message, 'response', displayName, picture);
roomClient.sendChatMessage(message);
roomClient.sendChatMessage(sendMessage);
this.setState({ message: '' });
}
}}
>
<SendIcon />
</IconButton>
</Paper>
);
}
}
setMessage('');
}
}}
>
<SendIcon />
</IconButton>
</Paper>
);
};
ChatInput.propTypes =
{
@@ -92,7 +92,7 @@ Message.propTypes =
self : PropTypes.bool,
picture : PropTypes.string,
text : PropTypes.string,
time : PropTypes.string,
time : PropTypes.object,
name : PropTypes.string,
classes : PropTypes.object.isRequired
};
@@ -2,6 +2,7 @@ import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import { FormattedTime } from 'react-intl';
import Message from './Message';
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
@@ -49,7 +50,7 @@ class MessageList extends React.Component
getTimeString(time)
{
return `${(time.getHours() < 10 ? '0' : '')}${time.getHours()}:${(time.getMinutes() < 10 ? '0' : '')}${time.getMinutes()}`;
return (<FormattedTime value={new Date(time)} />);
}
render()
@@ -65,8 +66,6 @@ class MessageList extends React.Component
{
chat.map((message, index) =>
{
const messageTime = new Date(message.time);
const picture = (message.sender === 'response' ?
message.picture : myPicture) || EmptyAvatar;
@@ -76,7 +75,7 @@ class MessageList extends React.Component
self={message.sender === 'client'}
picture={picture}
text={message.text}
time={this.getTimeString(messageTime)}
time={this.getTimeString(message.time)}
name={message.name}
/>
);
@@ -89,9 +88,9 @@ class MessageList extends React.Component
MessageList.propTypes =
{
chat : PropTypes.array,
myPicture : PropTypes.string,
classes : PropTypes.object.isRequired
chat : PropTypes.array,
myPicture : PropTypes.string,
classes : PropTypes.object.isRequired
};
const mapStateToProps = (state) =>
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withRoomContext } from '../../../RoomContext';
import { withStyles } from '@material-ui/core/styles';
import { FormattedMessage } from 'react-intl';
import magnet from 'magnet-uri';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
@@ -70,7 +71,10 @@ class File extends React.PureComponent
{ file.files &&
<Fragment>
<Typography className={classes.text}>
File finished downloading
<FormattedMessage
id='filesharing.finished'
defaultMessage='File finished downloading'
/>
</Typography>
{ file.files.map((sharedFile, i) => (
@@ -87,14 +91,23 @@ class File extends React.PureComponent
roomClient.saveFile(sharedFile);
}}
>
Save
<FormattedMessage
id='filesharing.save'
defaultMessage='Save'
/>
</Button>
</div>
))}
</Fragment>
}
<Typography className={classes.text}>
{ `${displayName} shared a file` }
<FormattedMessage
id='filesharing.sharedFile'
defaultMessage='{displayName} shared a file'
values={{
displayName
}}
/>
</Typography>
{ (!file.active && !file.files) &&
@@ -112,11 +125,17 @@ class File extends React.PureComponent
roomClient.handleDownload(magnetUri);
}}
>
Download
<FormattedMessage
id='filesharing.download'
defaultMessage='Download'
/>
</Button>
:
<Typography className={classes.text}>
Your browser does not support downloading files using WebTorrent.
<FormattedMessage
id='label.fileSharingUnsupported'
defaultMessage='File sharing not supported'
/>
</Typography>
}
</div>
@@ -124,8 +143,14 @@ class File extends React.PureComponent
{ file.timeout &&
<Typography className={classes.text}>
If this process takes a long time, there might not be anyone seeding
this torrent. Try asking someone to reupload the file that you want.
<FormattedMessage
id='filesharing.missingSeeds'
defaultMessage={
`If this process takes a long time, there might not
be anyone seeding this torrent. Try asking someone to
reupload the file that you want.`
}
/>
</Typography>
}
@@ -3,6 +3,7 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import * as appPropTypes from '../../appPropTypes';
import { withStyles } from '@material-ui/core/styles';
import { injectIntl } from 'react-intl';
import File from './File';
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
@@ -46,6 +47,7 @@ class FileList extends React.PureComponent
files,
me,
peers,
intl,
classes
} = this.props;
@@ -59,7 +61,10 @@ class FileList extends React.PureComponent
if (me.id === file.peerId)
{
displayName = 'You';
displayName = intl.formatMessage({
id : 'room.me',
defaultMessage : 'Me'
});
filePicture = me.picture;
}
else if (peers[file.peerId])
@@ -69,7 +74,10 @@ class FileList extends React.PureComponent
}
else
{
displayName = 'Unknown';
displayName = intl.formatMessage({
id : 'label.unknown',
defaultMessage : 'Unknown'
});
}
return (
@@ -91,6 +99,7 @@ FileList.propTypes =
files : PropTypes.object.isRequired,
me : appPropTypes.Me.isRequired,
peers : PropTypes.object.isRequired,
intl : PropTypes.object.isRequired,
classes : PropTypes.object.isRequired
};
@@ -117,4 +126,4 @@ export default connect(
);
}
}
)(withStyles(styles)(FileList));
)(withStyles(styles)(injectIntl(FileList)));
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../../RoomContext';
import { useIntl } from 'react-intl';
import FileList from './FileList';
import Paper from '@material-ui/core/Paper';
import Button from '@material-ui/core/Button';
@@ -26,58 +27,57 @@ const styles = (theme) =>
}
});
class FileSharing extends React.PureComponent
const FileSharing = (props) =>
{
constructor(props)
{
super(props);
const intl = useIntl();
this._fileInput = React.createRef();
}
handleFileChange = async (event) =>
const handleFileChange = async (event) =>
{
if (event.target.files.length > 0)
{
this.props.roomClient.shareFiles(event.target.files);
props.roomClient.shareFiles(event.target.files);
}
};
render()
{
const {
canShareFiles,
classes
} = this.props;
const {
canShareFiles,
classes
} = props;
const buttonDescription = canShareFiles ?
'Share file' : 'File sharing not supported';
const buttonDescription = canShareFiles ?
intl.formatMessage({
id : 'label.shareFile',
defaultMessage : 'Share file'
})
:
intl.formatMessage({
id : 'label.fileSharingUnsupported',
defaultMessage : 'File sharing not supported'
});
return (
<Paper className={classes.root}>
<input
ref={this._fileInput}
className={classes.input}
type='file'
onChange={this.handleFileChange}
id='share-files-button'
/>
<label htmlFor='share-files-button'>
<Button
variant='contained'
component='span'
className={classes.button}
disabled={!canShareFiles}
>
{buttonDescription}
</Button>
</label>
return (
<Paper className={classes.root}>
<input
className={classes.input}
type='file'
onChange={handleFileChange}
id='share-files-button'
/>
<label htmlFor='share-files-button'>
<Button
variant='contained'
component='span'
className={classes.button}
disabled={!canShareFiles}
>
{buttonDescription}
</Button>
</label>
<FileList />
</Paper>
);
}
}
<FileList />
</Paper>
);
};
FileSharing.propTypes = {
roomClient : PropTypes.any.isRequired,
@@ -3,6 +3,7 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import * as toolareaActions from '../../actions/toolareaActions';
import { useIntl } from 'react-intl';
import AppBar from '@material-ui/core/AppBar';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
@@ -44,6 +45,8 @@ const styles = (theme) =>
const MeetingDrawer = (props) =>
{
const intl = useIntl();
const {
currentToolTab,
unreadMessages,
@@ -72,18 +75,29 @@ const MeetingDrawer = (props) =>
<Tab
label={
<Badge color='secondary' badgeContent={unreadMessages}>
Chat
{intl.formatMessage({
id : 'label.chat',
defaultMessage : 'Chat'
})}
</Badge>
}
/>
<Tab
label={
<Badge color='secondary' badgeContent={unreadFiles}>
File sharing
{intl.formatMessage({
id : 'label.filesharing',
defaultMessage : 'File sharing'
})}
</Badge>
}
/>
<Tab label='Participants' />
<Tab
label={intl.formatMessage({
id : 'label.participants',
defaultMessage : 'Participants'
})}
/>
</Tabs>
<IconButton onClick={closeDrawer}>
{theme.direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
@@ -8,6 +8,7 @@ import classNames from 'classnames';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../../RoomContext';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import ListPeer from './ListPeer';
import ListMe from './ListMe';
import Volume from '../../Containers/Volume';
@@ -84,11 +85,21 @@ class ParticipantList extends React.PureComponent
return (
<div className={classes.root} ref={(node) => { this.node = node; }}>
<ul className={classes.list}>
<li className={classes.listheader}>Me:</li>
<li className={classes.listheader}>
<FormattedMessage
id='room.me'
defaultMessage='Me'
/>
</li>
<ListMe />
</ul>
<ul className={classes.list}>
<li className={classes.listheader}>Participants in Spotlight:</li>
<li className={classes.listheader}>
<FormattedMessage
id='room.spotlights'
defaultMessage='Participants in Spotlight'
/>
</li>
{ spotlightPeers.map((peer) => (
<li
key={peer.id}
@@ -104,7 +115,12 @@ class ParticipantList extends React.PureComponent
))}
</ul>
<ul className={classes.list}>
<li className={classes.listheader}>Passive Participants:</li>
<li className={classes.listheader}>
<FormattedMessage
id='room.passive'
defaultMessage='Passive Participants'
/>
</li>
{ passivePeers.map((peerId) => (
<li
key={peerId}
+6 -1
View File
@@ -7,6 +7,7 @@ import * as roomActions from '../actions/roomActions';
import * as toolareaActions from '../actions/toolareaActions';
import { idle } from '../utils';
import FullScreen from './FullScreen';
import { FormattedMessage } from 'react-intl';
import CookieConsent from 'react-cookie-consent';
import CssBaseline from '@material-ui/core/CssBaseline';
import SwipeableDrawer from '@material-ui/core/SwipeableDrawer';
@@ -152,7 +153,10 @@ class Room extends React.PureComponent
return (
<div className={classes.root}>
<CookieConsent>
This website uses cookies to enhance the user experience.
<FormattedMessage
id='room.cookieConsent'
defaultMessage='This website uses cookies to enhance the user experience'
/>
</CookieConsent>
<FullScreenView advancedMode={advancedMode} />
@@ -237,6 +241,7 @@ export default connect(
{
return (
prev.room === next.room &&
prev.settings.advancedMode === next.settings.advancedMode &&
prev.toolarea.toolAreaOpen === next.toolarea.toolAreaOpen
);
}
+103 -41
View File
@@ -6,6 +6,7 @@ import { withRoomContext } from '../../RoomContext';
import * as roomActions from '../../actions/roomActions';
import * as settingsActions from '../../actions/settingsActions';
import PropTypes from 'prop-types';
import { useIntl, FormattedMessage } from 'react-intl';
import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogActions from '@material-ui/core/DialogActions';
@@ -52,35 +53,6 @@ const styles = (theme) =>
}
});
const modes = [ {
value : 'democratic',
label : 'Democratic view'
}, {
value : 'filmstrip',
label : 'Filmstrip view'
} ];
const resolutions = [ {
value : 'low',
label : 'Low'
},
{
value : 'medium',
label : 'Medium'
},
{
value : 'high',
label : 'High (HD)'
},
{
value : 'veryhigh',
label : 'Very high (FHD)'
},
{
value : 'ultra',
label : 'Ultra (UHD)'
} ];
const Settings = ({
roomClient,
room,
@@ -92,6 +64,58 @@ const Settings = ({
classes
}) =>
{
const intl = useIntl();
const modes = [ {
value : 'democratic',
label : intl.formatMessage({
id : 'label.democratic',
defaultMessage : 'Democratic view'
})
}, {
value : 'filmstrip',
label : intl.formatMessage({
id : 'label.filmstrip',
defaultMessage : 'Filmstrip view'
})
} ];
const resolutions = [ {
value : 'low',
label : intl.formatMessage({
id : 'label.low',
defaultMessage : 'Low'
})
},
{
value : 'medium',
label : intl.formatMessage({
id : 'label.medium',
defaultMessage : 'Medium'
})
},
{
value : 'high',
label : intl.formatMessage({
id : 'label.high',
defaultMessage : 'High (HD)'
})
},
{
value : 'veryhigh',
label : intl.formatMessage({
id : 'label.veryHigh',
defaultMessage : 'Very high (FHD)'
})
},
{
value : 'ultra',
label : intl.formatMessage({
id : 'label.ultra',
defaultMessage : 'Ultra (UHD)'
})
} ];
let webcams;
if (me.webcamDevices)
@@ -115,7 +139,12 @@ const Settings = ({
paper : classes.dialogPaper
}}
>
<DialogTitle id='form-dialog-title'>Settings</DialogTitle>
<DialogTitle id='form-dialog-title'>
<FormattedMessage
id='settings.settings'
defaultMessage='Settings'
/>
</DialogTitle>
<form className={classes.setting} autoComplete='off'>
<FormControl className={classes.formControl}>
<Select
@@ -126,7 +155,10 @@ const Settings = ({
roomClient.changeWebcam(event.target.value);
}}
displayEmpty
name='Camera'
name={intl.formatMessage({
id : 'settings.camera',
defaultMessage : 'Camera'
})}
autoWidth
className={classes.selectEmpty}
disabled={webcams.length === 0 || me.webcamInProgress}
@@ -140,9 +172,15 @@ const Settings = ({
</Select>
<FormHelperText>
{ webcams.length > 0 ?
'Select video device'
intl.formatMessage({
id : 'settings.selectCamera',
defaultMessage : 'Select video device'
})
:
'Unable to select video device'
intl.formatMessage({
id : 'settings.cantSelectCamera',
defaultMessage : 'Unable to select video device'
})
}
</FormHelperText>
</FormControl>
@@ -157,7 +195,10 @@ const Settings = ({
roomClient.changeAudioDevice(event.target.value);
}}
displayEmpty
name='Audio device'
name={intl.formatMessage({
id : 'settings.audio',
defaultMessage : 'Audio device'
})}
autoWidth
className={classes.selectEmpty}
disabled={audioDevices.length === 0 || me.audioInProgress}
@@ -171,9 +212,15 @@ const Settings = ({
</Select>
<FormHelperText>
{ audioDevices.length > 0 ?
'Select audio device'
intl.formatMessage({
id : 'settings.selectAudio',
defaultMessage : 'Select audio device'
})
:
'Unable to select audio device'
intl.formatMessage({
id : 'settings.cantSelectAudio',
defaultMessage : 'Unable to select audio device'
})
}
</FormHelperText>
</FormControl>
@@ -201,7 +248,10 @@ const Settings = ({
})}
</Select>
<FormHelperText>
Select your video resolution
<FormattedMessage
id='settings.resolution'
defaultMessage='Select your video resolution'
/>
</FormHelperText>
</FormControl>
</form>
@@ -214,7 +264,10 @@ const Settings = ({
if (event.target.value)
handleChangeMode(event.target.value);
}}
name='Room layout'
name={intl.formatMessage({
id : 'settings.layout',
defaultMessage : 'Room layout'
})}
autoWidth
className={classes.selectEmpty}
>
@@ -228,18 +281,27 @@ const Settings = ({
})}
</Select>
<FormHelperText>
Select room layout
<FormattedMessage
id='settings.selectRoomLayout'
defaultMessage='Select room layout'
/>
</FormHelperText>
</FormControl>
</form>
<FormControlLabel
className={classes.setting}
control={<Checkbox checked={settings.advancedMode} onChange={onToggleAdvancedMode} value='advancedMode' />}
label='Advanced mode'
label={intl.formatMessage({
id : 'settings.advancedMode',
defaultMessage : 'Advanced mode'
})}
/>
<DialogActions>
<Button onClick={() => handleCloseSettings({ settingsOpen: false })} color='primary'>
Close
<FormattedMessage
id='label.close'
defaultMessage='Close'
/>
</Button>
</DialogActions>
</Dialog>