Merge branch 'feat-user-roles' into develop
commit
5071282d40
|
|
@ -417,7 +417,7 @@ export default class RoomClient
|
||||||
|
|
||||||
login()
|
login()
|
||||||
{
|
{
|
||||||
const url = `/auth/login?id=${this._peerId}`;
|
const url = `/auth/login?peerId=${this._peerId}&roomId=${this._roomId}`;
|
||||||
|
|
||||||
window.open(url, 'loginWindow');
|
window.open(url, 'loginWindow');
|
||||||
}
|
}
|
||||||
|
|
@ -433,16 +433,8 @@ export default class RoomClient
|
||||||
|
|
||||||
const { displayName, picture } = data;
|
const { displayName, picture } = data;
|
||||||
|
|
||||||
if (store.getState().room.state === 'connected')
|
store.dispatch(settingsActions.setDisplayName(displayName));
|
||||||
{
|
store.dispatch(meActions.setPicture(picture));
|
||||||
this.changeDisplayName(displayName);
|
|
||||||
this.changePicture(picture);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
store.dispatch(settingsActions.setDisplayName(displayName));
|
|
||||||
store.dispatch(meActions.setPicture(picture));
|
|
||||||
}
|
|
||||||
|
|
||||||
store.dispatch(meActions.loggedIn(true));
|
store.dispatch(meActions.loggedIn(true));
|
||||||
|
|
||||||
|
|
@ -1155,6 +1147,86 @@ export default class RoomClient
|
||||||
lobbyPeerActions.setLobbyPeerPromotionInProgress(peerId, false));
|
lobbyPeerActions.setLobbyPeerPromotionInProgress(peerId, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async kickPeer(peerId)
|
||||||
|
{
|
||||||
|
logger.debug('kickPeer() [peerId:"%s"]', peerId);
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
peerActions.setPeerKickInProgress(peerId, true));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await this.sendRequest('moderator:kickPeer', { peerId });
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
logger.error('kickPeer() failed: %o', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
peerActions.setPeerKickInProgress(peerId, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
async muteAllPeers()
|
||||||
|
{
|
||||||
|
logger.debug('muteAllPeers()');
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
roomActions.setMuteAllInProgress(true));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await this.sendRequest('moderator:muteAll');
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
logger.error('muteAllPeers() failed: %o', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
roomActions.setMuteAllInProgress(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
async stopAllPeerVideo()
|
||||||
|
{
|
||||||
|
logger.debug('stopAllPeerVideo()');
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
roomActions.setStopAllVideoInProgress(true));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await this.sendRequest('moderator:stopAllVideo');
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
logger.error('stopAllPeerVideo() failed: %o', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
roomActions.setStopAllVideoInProgress(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeMeeting()
|
||||||
|
{
|
||||||
|
logger.debug('closeMeeting()');
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
roomActions.setCloseMeetingInProgress(true));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await this.sendRequest('moderator:closeMeeting');
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
logger.error('closeMeeting() failed: %o', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(
|
||||||
|
roomActions.setCloseMeetingInProgress(false));
|
||||||
|
}
|
||||||
|
|
||||||
// type: mic/webcam/screen
|
// type: mic/webcam/screen
|
||||||
// mute: true/false
|
// mute: true/false
|
||||||
async modifyPeerConsumer(peerId, type, mute)
|
async modifyPeerConsumer(peerId, type, mute)
|
||||||
|
|
@ -1914,10 +1986,10 @@ export default class RoomClient
|
||||||
|
|
||||||
case 'newPeer':
|
case 'newPeer':
|
||||||
{
|
{
|
||||||
const { id, displayName, picture } = notification.data;
|
const { id, displayName, picture, roles } = notification.data;
|
||||||
|
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
peerActions.addPeer({ id, displayName, picture, consumers: [] }));
|
peerActions.addPeer({ id, displayName, picture, roles, consumers: [] }));
|
||||||
|
|
||||||
store.dispatch(requestActions.notify(
|
store.dispatch(requestActions.notify(
|
||||||
{
|
{
|
||||||
|
|
@ -2017,6 +2089,96 @@ export default class RoomClient
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'moderator:mute':
|
||||||
|
{
|
||||||
|
// const { peerId } = notification.data;
|
||||||
|
|
||||||
|
if (this._micProducer && !this._micProducer.paused)
|
||||||
|
{
|
||||||
|
this.muteMic();
|
||||||
|
|
||||||
|
store.dispatch(requestActions.notify(
|
||||||
|
{
|
||||||
|
text : intl.formatMessage({
|
||||||
|
id : 'moderator.mute',
|
||||||
|
defaultMessage : 'Moderator muted your microphone'
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'moderator:stopVideo':
|
||||||
|
{
|
||||||
|
// const { peerId } = notification.data;
|
||||||
|
|
||||||
|
this.disableWebcam();
|
||||||
|
this.disableScreenSharing();
|
||||||
|
|
||||||
|
store.dispatch(requestActions.notify(
|
||||||
|
{
|
||||||
|
text : intl.formatMessage({
|
||||||
|
id : 'moderator.mute',
|
||||||
|
defaultMessage : 'Moderator stopped your video'
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'moderator:kick':
|
||||||
|
{
|
||||||
|
// Need some feedback
|
||||||
|
this.close();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'gotRole':
|
||||||
|
{
|
||||||
|
const { peerId, role } = notification.data;
|
||||||
|
|
||||||
|
if (peerId === this._peerId)
|
||||||
|
{
|
||||||
|
store.dispatch(meActions.addRole(role));
|
||||||
|
|
||||||
|
store.dispatch(requestActions.notify(
|
||||||
|
{
|
||||||
|
text : intl.formatMessage({
|
||||||
|
id : 'roles.gotRole',
|
||||||
|
defaultMessage : `You got the role: ${role}`
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
store.dispatch(peerActions.addPeerRole(peerId, role));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'lostRole':
|
||||||
|
{
|
||||||
|
const { peerId, role } = notification.data;
|
||||||
|
|
||||||
|
if (peerId === this._peerId)
|
||||||
|
{
|
||||||
|
store.dispatch(meActions.removeRole(role));
|
||||||
|
|
||||||
|
store.dispatch(requestActions.notify(
|
||||||
|
{
|
||||||
|
text : intl.formatMessage({
|
||||||
|
id : 'roles.lostRole',
|
||||||
|
defaultMessage : `You lost the role: ${role}`
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
store.dispatch(peerActions.removePeerRole(peerId, role));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
logger.error(
|
logger.error(
|
||||||
|
|
@ -2195,7 +2357,7 @@ export default class RoomClient
|
||||||
canShareFiles : this._torrentSupport
|
canShareFiles : this._torrentSupport
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { peers } = await this.sendRequest(
|
const { roles, peers } = await this.sendRequest(
|
||||||
'join',
|
'join',
|
||||||
{
|
{
|
||||||
displayName : displayName,
|
displayName : displayName,
|
||||||
|
|
@ -2203,7 +2365,25 @@ export default class RoomClient
|
||||||
rtpCapabilities : this._mediasoupDevice.rtpCapabilities
|
rtpCapabilities : this._mediasoupDevice.rtpCapabilities
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.debug('_joinRoom() joined, got peers [peers:"%o"]', peers);
|
logger.debug('_joinRoom() joined [peers:"%o", roles:"%o"]', peers, roles);
|
||||||
|
|
||||||
|
const myRoles = store.getState().me.roles;
|
||||||
|
|
||||||
|
for (const role of roles)
|
||||||
|
{
|
||||||
|
if (!myRoles.includes(role))
|
||||||
|
{
|
||||||
|
store.dispatch(meActions.addRole(role));
|
||||||
|
|
||||||
|
store.dispatch(requestActions.notify(
|
||||||
|
{
|
||||||
|
text : intl.formatMessage({
|
||||||
|
id : 'roles.gotRole',
|
||||||
|
defaultMessage : `You got the role: ${role}`
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const peer of peers)
|
for (const peer of peers)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,18 @@ export const loggedIn = (flag) =>
|
||||||
payload : { flag }
|
payload : { flag }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const addRole = (role) =>
|
||||||
|
({
|
||||||
|
type : 'ADD_ROLE',
|
||||||
|
payload : { role }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const removeRole = (role) =>
|
||||||
|
({
|
||||||
|
type : 'REMOVE_ROLE',
|
||||||
|
payload : { role }
|
||||||
|
});
|
||||||
|
|
||||||
export const setPicture = (picture) =>
|
export const setPicture = (picture) =>
|
||||||
({
|
({
|
||||||
type : 'SET_PICTURE',
|
type : 'SET_PICTURE',
|
||||||
|
|
|
||||||
|
|
@ -45,3 +45,22 @@ export const setPeerPicture = (peerId, picture) =>
|
||||||
type : 'SET_PEER_PICTURE',
|
type : 'SET_PEER_PICTURE',
|
||||||
payload : { peerId, picture }
|
payload : { peerId, picture }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const addPeerRole = (peerId, role) =>
|
||||||
|
({
|
||||||
|
type : 'ADD_PEER_ROLE',
|
||||||
|
payload : { peerId, role }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const removePeerRole = (peerId, role) =>
|
||||||
|
({
|
||||||
|
type : 'REMOVE_PEER_ROLE',
|
||||||
|
payload : { peerId, role }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setPeerKickInProgress = (peerId, flag) =>
|
||||||
|
({
|
||||||
|
type : 'SET_PEER_KICK_IN_PROGRESS',
|
||||||
|
payload : { peerId, flag }
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -110,3 +110,21 @@ export const toggleConsumerFullscreen = (consumerId) =>
|
||||||
type : 'TOGGLE_FULLSCREEN_CONSUMER',
|
type : 'TOGGLE_FULLSCREEN_CONSUMER',
|
||||||
payload : { consumerId }
|
payload : { consumerId }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setMuteAllInProgress = (flag) =>
|
||||||
|
({
|
||||||
|
type : 'MUTE_ALL_IN_PROGRESS',
|
||||||
|
payload : { flag }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setStopAllVideoInProgress = (flag) =>
|
||||||
|
({
|
||||||
|
type : 'STOP_ALL_VIDEO_IN_PROGRESS',
|
||||||
|
payload : { flag }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setCloseMeetingInProgress = (flag) =>
|
||||||
|
({
|
||||||
|
type : 'CLOSE_MEETING_IN_PROGRESS',
|
||||||
|
payload : { flag }
|
||||||
|
});
|
||||||
|
|
@ -1,21 +1,15 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import { withRoomContext } from '../RoomContext';
|
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useIntl, FormattedMessage } from 'react-intl';
|
import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
import randomString from 'random-string';
|
import randomString from 'random-string';
|
||||||
import Dialog from '@material-ui/core/Dialog';
|
import Dialog from '@material-ui/core/Dialog';
|
||||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
|
||||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
|
||||||
import Avatar from '@material-ui/core/Avatar';
|
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import TextField from '@material-ui/core/TextField';
|
import TextField from '@material-ui/core/TextField';
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
|
||||||
import CookieConsent from 'react-cookie-consent';
|
import CookieConsent from 'react-cookie-consent';
|
||||||
import MuiDialogTitle from '@material-ui/core/DialogTitle';
|
import MuiDialogTitle from '@material-ui/core/DialogTitle';
|
||||||
import MuiDialogContent from '@material-ui/core/DialogContent';
|
import MuiDialogContent from '@material-ui/core/DialogContent';
|
||||||
|
|
@ -88,63 +82,12 @@ const styles = (theme) =>
|
||||||
|
|
||||||
const DialogTitle = withStyles(styles)((props) =>
|
const DialogTitle = withStyles(styles)((props) =>
|
||||||
{
|
{
|
||||||
const [ open, setOpen ] = useState(false);
|
const { children, classes, ...other } = props;
|
||||||
|
|
||||||
const intl = useIntl();
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
const openTimer = setTimeout(() => setOpen(true), 1000);
|
|
||||||
const closeTimer = setTimeout(() => setOpen(false), 4000);
|
|
||||||
|
|
||||||
return () =>
|
|
||||||
{
|
|
||||||
clearTimeout(openTimer);
|
|
||||||
clearTimeout(closeTimer);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const { children, classes, myPicture, onLogin, ...other } = props;
|
|
||||||
|
|
||||||
const handleTooltipClose = () =>
|
|
||||||
{
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTooltipOpen = () =>
|
|
||||||
{
|
|
||||||
setOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MuiDialogTitle disableTypography className={classes.dialogTitle} {...other}>
|
<MuiDialogTitle disableTypography className={classes.dialogTitle} {...other}>
|
||||||
{ window.config && window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
|
{ window.config && window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }
|
||||||
<Typography variant='h5'>{children}</Typography>
|
<Typography variant='h5'>{children}</Typography>
|
||||||
{ window.config && window.config.loginEnabled &&
|
|
||||||
<Tooltip
|
|
||||||
onClose={handleTooltipClose}
|
|
||||||
onOpen={handleTooltipOpen}
|
|
||||||
open={open}
|
|
||||||
title={intl.formatMessage({
|
|
||||||
id : 'tooltip.login',
|
|
||||||
defaultMessage : 'Click to log in'
|
|
||||||
})}
|
|
||||||
placement='left'
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
aria-label='Account'
|
|
||||||
className={classes.loginButton}
|
|
||||||
color='inherit'
|
|
||||||
onClick={onLogin}
|
|
||||||
>
|
|
||||||
{ myPicture ?
|
|
||||||
<Avatar src={myPicture} className={classes.largeAvatar} />
|
|
||||||
:
|
|
||||||
<AccountCircle className={classes.largeIcon} />
|
|
||||||
}
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
</MuiDialogTitle>
|
</MuiDialogTitle>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -165,9 +108,6 @@ const DialogActions = withStyles((theme) => ({
|
||||||
}))(MuiDialogActions);
|
}))(MuiDialogActions);
|
||||||
|
|
||||||
const ChooseRoom = ({
|
const ChooseRoom = ({
|
||||||
roomClient,
|
|
||||||
loggedIn,
|
|
||||||
myPicture,
|
|
||||||
classes
|
classes
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
|
|
@ -184,13 +124,7 @@ const ChooseRoom = ({
|
||||||
paper : classes.dialogPaper
|
paper : classes.dialogPaper
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle
|
<DialogTitle>
|
||||||
myPicture={myPicture}
|
|
||||||
onLogin={() =>
|
|
||||||
{
|
|
||||||
loggedIn ? roomClient.logout() : roomClient.login();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ window.config && window.config.title ? window.config.title : 'Multiparty meeting' }
|
{ window.config && window.config.title ? window.config.title : 'Multiparty meeting' }
|
||||||
<hr />
|
<hr />
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
@ -258,34 +192,7 @@ const ChooseRoom = ({
|
||||||
|
|
||||||
ChooseRoom.propTypes =
|
ChooseRoom.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.any.isRequired,
|
classes : PropTypes.object.isRequired
|
||||||
loginEnabled : PropTypes.bool.isRequired,
|
|
||||||
loggedIn : PropTypes.bool.isRequired,
|
|
||||||
myPicture : PropTypes.string,
|
|
||||||
classes : PropTypes.object.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
export default withStyles(styles)(ChooseRoom);
|
||||||
{
|
|
||||||
return {
|
|
||||||
loginEnabled : state.me.loginEnabled,
|
|
||||||
loggedIn : state.me.loggedIn,
|
|
||||||
myPicture : state.me.picture
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withRoomContext(connect(
|
|
||||||
mapStateToProps,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
{
|
|
||||||
areStatesEqual : (next, prev) =>
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
prev.me.loginEnabled === next.me.loginEnabled &&
|
|
||||||
prev.me.loggedIn === next.me.loggedIn &&
|
|
||||||
prev.me.picture === next.me.picture
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)(withStyles(styles)(ChooseRoom)));
|
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { withRoomContext } from '../../../RoomContext';
|
||||||
|
import { useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import Button from '@material-ui/core/Button';
|
||||||
|
|
||||||
|
const styles = (theme) =>
|
||||||
|
({
|
||||||
|
root :
|
||||||
|
{
|
||||||
|
padding : theme.spacing(1),
|
||||||
|
width : '100%',
|
||||||
|
overflow : 'hidden',
|
||||||
|
cursor : 'auto',
|
||||||
|
display : 'flex'
|
||||||
|
},
|
||||||
|
actionButtons :
|
||||||
|
{
|
||||||
|
display : 'flex'
|
||||||
|
},
|
||||||
|
divider :
|
||||||
|
{
|
||||||
|
marginLeft : theme.spacing(2)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ListModerator = (props) =>
|
||||||
|
{
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const {
|
||||||
|
roomClient,
|
||||||
|
room,
|
||||||
|
classes
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Button
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'room.muteAll',
|
||||||
|
defaultMessage : 'Mute all'
|
||||||
|
})}
|
||||||
|
className={classes.actionButton}
|
||||||
|
variant='contained'
|
||||||
|
color='secondary'
|
||||||
|
disabled={room.muteAllInProgress}
|
||||||
|
onClick={() => roomClient.muteAllPeers()}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='room.muteAll'
|
||||||
|
defaultMessage='Mute all'
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
<div className={classes.divider} />
|
||||||
|
<Button
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'room.stopAllVideo',
|
||||||
|
defaultMessage : 'Stop all video'
|
||||||
|
})}
|
||||||
|
className={classes.actionButton}
|
||||||
|
variant='contained'
|
||||||
|
color='secondary'
|
||||||
|
disabled={room.stopAllVideoInProgress}
|
||||||
|
onClick={() => roomClient.stopAllPeerVideo()}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='room.stopAllVideo'
|
||||||
|
defaultMessage='Stop all video'
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
<div className={classes.divider} />
|
||||||
|
<Button
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'room.closeMeeting',
|
||||||
|
defaultMessage : 'Close meeting'
|
||||||
|
})}
|
||||||
|
className={classes.actionButton}
|
||||||
|
variant='contained'
|
||||||
|
color='secondary'
|
||||||
|
disabled={room.closeMeetingInProgress}
|
||||||
|
onClick={() => roomClient.closeMeeting()}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='room.closeMeeting'
|
||||||
|
defaultMessage='Close meeting'
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ListModerator.propTypes =
|
||||||
|
{
|
||||||
|
roomClient : PropTypes.any.isRequired,
|
||||||
|
room : PropTypes.object.isRequired,
|
||||||
|
classes : PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
room : state.room
|
||||||
|
});
|
||||||
|
|
||||||
|
export default withRoomContext(connect(
|
||||||
|
mapStateToProps,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
areStatesEqual : (next, prev) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
prev.room === next.room
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)(withStyles(styles)(ListModerator)));
|
||||||
|
|
@ -12,6 +12,7 @@ import MicIcon from '@material-ui/icons/Mic';
|
||||||
import MicOffIcon from '@material-ui/icons/MicOff';
|
import MicOffIcon from '@material-ui/icons/MicOff';
|
||||||
import ScreenIcon from '@material-ui/icons/ScreenShare';
|
import ScreenIcon from '@material-ui/icons/ScreenShare';
|
||||||
import ScreenOffIcon from '@material-ui/icons/StopScreenShare';
|
import ScreenOffIcon from '@material-ui/icons/StopScreenShare';
|
||||||
|
import ExitIcon from '@material-ui/icons/ExitToApp';
|
||||||
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
|
import EmptyAvatar from '../../../images/avatar-empty.jpeg';
|
||||||
import HandIcon from '../../../images/icon-hand-white.svg';
|
import HandIcon from '../../../images/icon-hand-white.svg';
|
||||||
|
|
||||||
|
|
@ -91,40 +92,6 @@ const styles = (theme) =>
|
||||||
flexDirection : 'row',
|
flexDirection : 'row',
|
||||||
justifyContent : 'flex-start',
|
justifyContent : 'flex-start',
|
||||||
alignItems : 'center'
|
alignItems : 'center'
|
||||||
},
|
|
||||||
button :
|
|
||||||
{
|
|
||||||
flex : '0 0 auto',
|
|
||||||
margin : '0.3rem',
|
|
||||||
borderRadius : 2,
|
|
||||||
backgroundColor : 'rgba(0, 0, 0, 0.5)',
|
|
||||||
cursor : 'pointer',
|
|
||||||
transitionProperty : 'opacity, background-color',
|
|
||||||
transitionDuration : '0.15s',
|
|
||||||
width : 'var(--media-control-button-size)',
|
|
||||||
height : 'var(--media-control-button-size)',
|
|
||||||
opacity : 0.85,
|
|
||||||
'&:hover' :
|
|
||||||
{
|
|
||||||
opacity : 1
|
|
||||||
},
|
|
||||||
'&.unsupported' :
|
|
||||||
{
|
|
||||||
pointerEvents : 'none'
|
|
||||||
},
|
|
||||||
'&.disabled' :
|
|
||||||
{
|
|
||||||
pointerEvents : 'none',
|
|
||||||
backgroundColor : 'var(--media-control-botton-disabled)'
|
|
||||||
},
|
|
||||||
'&.on' :
|
|
||||||
{
|
|
||||||
backgroundColor : 'var(--media-control-botton-on)'
|
|
||||||
},
|
|
||||||
'&.off' :
|
|
||||||
{
|
|
||||||
backgroundColor : 'var(--media-control-botton-off)'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -134,6 +101,7 @@ const ListPeer = (props) =>
|
||||||
|
|
||||||
const {
|
const {
|
||||||
roomClient,
|
roomClient,
|
||||||
|
isModerator,
|
||||||
peer,
|
peer,
|
||||||
micConsumer,
|
micConsumer,
|
||||||
screenConsumer,
|
screenConsumer,
|
||||||
|
|
@ -185,9 +153,8 @@ const ListPeer = (props) =>
|
||||||
})}
|
})}
|
||||||
color={ screenVisible ? 'primary' : 'secondary'}
|
color={ screenVisible ? 'primary' : 'secondary'}
|
||||||
disabled={ peer.peerScreenInProgress }
|
disabled={ peer.peerScreenInProgress }
|
||||||
onClick={(e) =>
|
onClick={() =>
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
|
||||||
screenVisible ?
|
screenVisible ?
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'screen', true) :
|
roomClient.modifyPeerConsumer(peer.id, 'screen', true) :
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'screen', false);
|
roomClient.modifyPeerConsumer(peer.id, 'screen', false);
|
||||||
|
|
@ -207,9 +174,8 @@ const ListPeer = (props) =>
|
||||||
})}
|
})}
|
||||||
color={ micEnabled ? 'primary' : 'secondary'}
|
color={ micEnabled ? 'primary' : 'secondary'}
|
||||||
disabled={ peer.peerAudioInProgress }
|
disabled={ peer.peerAudioInProgress }
|
||||||
onClick={(e) =>
|
onClick={() =>
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
|
||||||
micEnabled ?
|
micEnabled ?
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
|
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
|
||||||
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
|
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
|
||||||
|
|
@ -221,6 +187,21 @@ const ListPeer = (props) =>
|
||||||
<MicOffIcon />
|
<MicOffIcon />
|
||||||
}
|
}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
{ isModerator &&
|
||||||
|
<IconButton
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id : 'tooltip.kickParticipant',
|
||||||
|
defaultMessage : 'Kick out participant'
|
||||||
|
})}
|
||||||
|
disabled={ peer.peerKickInProgress }
|
||||||
|
onClick={() =>
|
||||||
|
{
|
||||||
|
roomClient.kickPeer(peer.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ExitIcon />
|
||||||
|
</IconButton>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -230,6 +211,7 @@ ListPeer.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
|
isModerator : PropTypes.bool,
|
||||||
peer : appPropTypes.Peer.isRequired,
|
peer : appPropTypes.Peer.isRequired,
|
||||||
micConsumer : appPropTypes.Consumer,
|
micConsumer : appPropTypes.Consumer,
|
||||||
webcamConsumer : appPropTypes.Consumer,
|
webcamConsumer : appPropTypes.Consumer,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@ import PropTypes from 'prop-types';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import ListPeer from './ListPeer';
|
import ListPeer from './ListPeer';
|
||||||
import ListMe from './ListMe';
|
import ListMe from './ListMe';
|
||||||
|
import ListModerator from './ListModerator';
|
||||||
import Volume from '../../Containers/Volume';
|
import Volume from '../../Containers/Volume';
|
||||||
|
import * as userRoles from '../../../reducers/userRoles';
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
|
|
@ -76,6 +78,7 @@ class ParticipantList extends React.PureComponent
|
||||||
const {
|
const {
|
||||||
roomClient,
|
roomClient,
|
||||||
advancedMode,
|
advancedMode,
|
||||||
|
isModerator,
|
||||||
passivePeers,
|
passivePeers,
|
||||||
selectedPeerId,
|
selectedPeerId,
|
||||||
spotlightPeers,
|
spotlightPeers,
|
||||||
|
|
@ -84,6 +87,17 @@ class ParticipantList extends React.PureComponent
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root} ref={(node) => { this.node = node; }}>
|
<div className={classes.root} ref={(node) => { this.node = node; }}>
|
||||||
|
{ isModerator &&
|
||||||
|
<ul className={classes.list}>
|
||||||
|
<li className={classes.listheader}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='room.moderatoractions'
|
||||||
|
defaultMessage='Moderator actions'
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<ListModerator />
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
<ul className={classes.list}>
|
<ul className={classes.list}>
|
||||||
<li className={classes.listheader}>
|
<li className={classes.listheader}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
|
@ -108,7 +122,7 @@ class ParticipantList extends React.PureComponent
|
||||||
})}
|
})}
|
||||||
onClick={() => roomClient.setSelectedPeer(peerId)}
|
onClick={() => roomClient.setSelectedPeer(peerId)}
|
||||||
>
|
>
|
||||||
<ListPeer id={peerId} advancedMode={advancedMode}>
|
<ListPeer id={peerId} advancedMode={advancedMode} isModerator={isModerator}>
|
||||||
<Volume small id={peerId} />
|
<Volume small id={peerId} />
|
||||||
</ListPeer>
|
</ListPeer>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -129,7 +143,11 @@ class ParticipantList extends React.PureComponent
|
||||||
})}
|
})}
|
||||||
onClick={() => roomClient.setSelectedPeer(peerId)}
|
onClick={() => roomClient.setSelectedPeer(peerId)}
|
||||||
>
|
>
|
||||||
<ListPeer id={peerId} advancedMode={advancedMode} />
|
<ListPeer
|
||||||
|
id={peerId}
|
||||||
|
advancedMode={advancedMode}
|
||||||
|
isModerator={isModerator}
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -142,6 +160,7 @@ ParticipantList.propTypes =
|
||||||
{
|
{
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
|
isModerator : PropTypes.bool,
|
||||||
passivePeers : PropTypes.array,
|
passivePeers : PropTypes.array,
|
||||||
selectedPeerId : PropTypes.string,
|
selectedPeerId : PropTypes.string,
|
||||||
spotlightPeers : PropTypes.array,
|
spotlightPeers : PropTypes.array,
|
||||||
|
|
@ -151,6 +170,8 @@ ParticipantList.propTypes =
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
isModerator : state.me.roles.includes(userRoles.MODERATOR) ||
|
||||||
|
state.me.roles.includes(userRoles.ADMIN),
|
||||||
passivePeers : passivePeersSelector(state),
|
passivePeers : passivePeersSelector(state),
|
||||||
selectedPeerId : state.room.selectedPeerId,
|
selectedPeerId : state.room.selectedPeerId,
|
||||||
spotlightPeers : spotlightPeersSelector(state)
|
spotlightPeers : spotlightPeersSelector(state)
|
||||||
|
|
@ -165,6 +186,7 @@ const ParticipantListContainer = withRoomContext(connect(
|
||||||
areStatesEqual : (next, prev) =>
|
areStatesEqual : (next, prev) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
|
prev.me.roles === next.me.roles &&
|
||||||
prev.peers === next.peers &&
|
prev.peers === next.peers &&
|
||||||
prev.room.spotlights === next.room.spotlights &&
|
prev.room.spotlights === next.room.spotlights &&
|
||||||
prev.room.selectedPeerId === next.room.selectedPeerId
|
prev.room.selectedPeerId === next.room.selectedPeerId
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
|
import * as userRoles from './userRoles';
|
||||||
|
|
||||||
const initialState =
|
const initialState =
|
||||||
{
|
{
|
||||||
id : null,
|
id : null,
|
||||||
picture : null,
|
picture : null,
|
||||||
isMobile : false,
|
isMobile : false,
|
||||||
|
roles : [ userRoles.ALL ],
|
||||||
canSendMic : false,
|
canSendMic : false,
|
||||||
canSendWebcam : false,
|
canSendWebcam : false,
|
||||||
canShareScreen : false,
|
canShareScreen : false,
|
||||||
|
|
@ -49,6 +52,24 @@ const me = (state = initialState, action) =>
|
||||||
return { ...state, loggedIn: flag };
|
return { ...state, loggedIn: flag };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'ADD_ROLE':
|
||||||
|
{
|
||||||
|
if (state.roles.includes(action.payload.role))
|
||||||
|
return state;
|
||||||
|
|
||||||
|
const roles = [ ...state.roles, action.payload.role ];
|
||||||
|
|
||||||
|
return { ...state, roles };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'REMOVE_ROLE':
|
||||||
|
{
|
||||||
|
const roles = state.roles.filter((role) =>
|
||||||
|
role !== action.payload.role);
|
||||||
|
|
||||||
|
return { ...state, roles };
|
||||||
|
}
|
||||||
|
|
||||||
case 'SET_PICTURE':
|
case 'SET_PICTURE':
|
||||||
return { ...state, picture: action.payload.picture };
|
return { ...state, picture: action.payload.picture };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ const peer = (state = {}, action) =>
|
||||||
case 'SET_PEER_SCREEN_IN_PROGRESS':
|
case 'SET_PEER_SCREEN_IN_PROGRESS':
|
||||||
return { ...state, peerScreenInProgress: action.payload.flag };
|
return { ...state, peerScreenInProgress: action.payload.flag };
|
||||||
|
|
||||||
|
case 'SET_PEER_KICK_IN_PROGRESS':
|
||||||
|
return { ...state, peerKickInProgress: action.payload.flag };
|
||||||
|
|
||||||
case 'SET_PEER_RAISE_HAND_STATE':
|
case 'SET_PEER_RAISE_HAND_STATE':
|
||||||
return { ...state, raiseHandState: action.payload.raiseHandState };
|
return { ...state, raiseHandState: action.payload.raiseHandState };
|
||||||
|
|
||||||
|
|
@ -40,6 +43,21 @@ const peer = (state = {}, action) =>
|
||||||
return { ...state, picture: action.payload.picture };
|
return { ...state, picture: action.payload.picture };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'ADD_PEER_ROLE':
|
||||||
|
{
|
||||||
|
const roles = [ ...state.roles, action.payload.role ];
|
||||||
|
|
||||||
|
return { ...state, roles };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'REMOVE_PEER_ROLE':
|
||||||
|
{
|
||||||
|
const roles = state.roles.filter((role) =>
|
||||||
|
role !== action.payload.role);
|
||||||
|
|
||||||
|
return { ...state, roles };
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
@ -71,6 +89,8 @@ const peers = (state = {}, action) =>
|
||||||
case 'SET_PEER_RAISE_HAND_STATE':
|
case 'SET_PEER_RAISE_HAND_STATE':
|
||||||
case 'SET_PEER_PICTURE':
|
case 'SET_PEER_PICTURE':
|
||||||
case 'ADD_CONSUMER':
|
case 'ADD_CONSUMER':
|
||||||
|
case 'ADD_PEER_ROLE':
|
||||||
|
case 'REMOVE_PEER_ROLE':
|
||||||
{
|
{
|
||||||
const oldPeer = state[action.payload.peerId];
|
const oldPeer = state[action.payload.peerId];
|
||||||
|
|
||||||
|
|
@ -82,6 +102,7 @@ const peers = (state = {}, action) =>
|
||||||
return { ...state, [oldPeer.id]: peer(oldPeer, action) };
|
return { ...state, [oldPeer.id]: peer(oldPeer, action) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'SET_PEER_KICK_IN_PROGRESS':
|
||||||
case 'REMOVE_CONSUMER':
|
case 'REMOVE_CONSUMER':
|
||||||
{
|
{
|
||||||
const oldPeer = state[action.payload.peerId];
|
const oldPeer = state[action.payload.peerId];
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,27 @@
|
||||||
const initialState =
|
const initialState =
|
||||||
{
|
{
|
||||||
name : '',
|
name : '',
|
||||||
state : 'new', // new/connecting/connected/disconnected/closed,
|
state : 'new', // new/connecting/connected/disconnected/closed,
|
||||||
locked : false,
|
locked : false,
|
||||||
inLobby : false,
|
inLobby : false,
|
||||||
signInRequired : false,
|
signInRequired : false,
|
||||||
accessCode : '', // access code to the room if locked and joinByAccessCode == true
|
accessCode : '', // access code to the room if locked and joinByAccessCode == true
|
||||||
joinByAccessCode : true, // if true: accessCode is a possibility to open the room
|
joinByAccessCode : true, // if true: accessCode is a possibility to open the room
|
||||||
activeSpeakerId : null,
|
activeSpeakerId : null,
|
||||||
torrentSupport : false,
|
torrentSupport : false,
|
||||||
showSettings : false,
|
showSettings : false,
|
||||||
fullScreenConsumer : null, // ConsumerID
|
fullScreenConsumer : null, // ConsumerID
|
||||||
windowConsumer : null, // ConsumerID
|
windowConsumer : null, // ConsumerID
|
||||||
toolbarsVisible : true,
|
toolbarsVisible : true,
|
||||||
mode : 'democratic',
|
mode : 'democratic',
|
||||||
selectedPeerId : null,
|
selectedPeerId : null,
|
||||||
spotlights : [],
|
spotlights : [],
|
||||||
settingsOpen : false,
|
settingsOpen : false,
|
||||||
lockDialogOpen : false,
|
lockDialogOpen : false,
|
||||||
joined : false
|
joined : false,
|
||||||
|
muteAllInProgress : false,
|
||||||
|
stopAllVideoInProgress : false,
|
||||||
|
closeMeetingInProgress : false
|
||||||
};
|
};
|
||||||
|
|
||||||
const room = (state = initialState, action) =>
|
const room = (state = initialState, action) =>
|
||||||
|
|
@ -110,7 +113,7 @@ const room = (state = initialState, action) =>
|
||||||
|
|
||||||
case 'TOGGLE_JOINED':
|
case 'TOGGLE_JOINED':
|
||||||
{
|
{
|
||||||
const joined = !state.joined;
|
const joined = true;
|
||||||
|
|
||||||
return { ...state, joined };
|
return { ...state, joined };
|
||||||
}
|
}
|
||||||
|
|
@ -163,6 +166,15 @@ const room = (state = initialState, action) =>
|
||||||
return { ...state, spotlights };
|
return { ...state, spotlights };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'MUTE_ALL_IN_PROGRESS':
|
||||||
|
return { ...state, muteAllInProgress: action.payload.flag };
|
||||||
|
|
||||||
|
case 'STOP_ALL_VIDEO_IN_PROGRESS':
|
||||||
|
return { ...state, stopAllVideoInProgress: action.payload.flag };
|
||||||
|
|
||||||
|
case 'CLOSE_MEETING_IN_PROGRESS':
|
||||||
|
return { ...state, closeMeetingInProgress: action.payload.flag };
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const ADMIN = 'admin';
|
||||||
|
export const MODERATOR = 'moderator';
|
||||||
|
export const AUTHENTICATED = 'authenticated';
|
||||||
|
export const ALL = 'normal';
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"room.spotlights": "Spotlight中的参与者",
|
"room.spotlights": "Spotlight中的参与者",
|
||||||
"room.passive": "被动参与者",
|
"room.passive": "被动参与者",
|
||||||
"room.videoPaused": "该视频已暂停",
|
"room.videoPaused": "该视频已暂停",
|
||||||
|
"room.muteAll": null,
|
||||||
|
"room.stopAllVideo": null,
|
||||||
|
"room.closeMeeting": null,
|
||||||
|
|
||||||
"tooltip.login": "登录",
|
"tooltip.login": "登录",
|
||||||
"tooltip.logout": "注销",
|
"tooltip.logout": "注销",
|
||||||
|
|
@ -60,6 +63,7 @@
|
||||||
"tooltip.lobby": "显示大厅",
|
"tooltip.lobby": "显示大厅",
|
||||||
"tooltip.settings": "显示设置",
|
"tooltip.settings": "显示设置",
|
||||||
"tooltip.participants": "显示参加者",
|
"tooltip.participants": "显示参加者",
|
||||||
|
"tooltip.kickParticipant": null,
|
||||||
|
|
||||||
"label.roomName": "房间名称",
|
"label.roomName": "房间名称",
|
||||||
"label.chooseRoomButton": "继续",
|
"label.chooseRoomButton": "继续",
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"socket.reconnected": "Verbindung wieder herges|tellt",
|
"socket.reconnected": "Verbindung wieder herges|tellt",
|
||||||
"socket.requestError": "Fehler bei Serveranfrage",
|
"socket.requestError": "Fehler bei Serveranfrage",
|
||||||
|
|
||||||
"room.chooseRoom": "Choose the name of the room you would like to join",
|
"room.chooseRoom": null,
|
||||||
"room.cookieConsent": "Diese Seite verwendet Cookies, um die Benutzerfreundlichkeit zu erhöhen",
|
"room.cookieConsent": "Diese Seite verwendet Cookies, um die Benutzerfreundlichkeit zu erhöhen",
|
||||||
"room.consentUnderstand": "I understand",
|
"room.consentUnderstand": "I understand",
|
||||||
"room.joined": "Konferenzraum betreten",
|
"room.joined": "Konferenzraum betreten",
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"room.spotlights": "Aktive Teinehmer",
|
"room.spotlights": "Aktive Teinehmer",
|
||||||
"room.passive": "Passive Teilnehmer",
|
"room.passive": "Passive Teilnehmer",
|
||||||
"room.videoPaused": "Video gestoppt",
|
"room.videoPaused": "Video gestoppt",
|
||||||
|
"room.muteAll": null,
|
||||||
|
"room.stopAllVideo": null,
|
||||||
|
"room.closeMeeting": null,
|
||||||
|
|
||||||
"tooltip.login": "Anmelden",
|
"tooltip.login": "Anmelden",
|
||||||
"tooltip.logout": "Abmelden",
|
"tooltip.logout": "Abmelden",
|
||||||
|
|
@ -60,9 +63,10 @@
|
||||||
"tooltip.lobby": "Warteraum",
|
"tooltip.lobby": "Warteraum",
|
||||||
"tooltip.settings": "Einstellungen",
|
"tooltip.settings": "Einstellungen",
|
||||||
"tooltip.participants": "Teilnehmer",
|
"tooltip.participants": "Teilnehmer",
|
||||||
|
"tooltip.kickParticipant": null,
|
||||||
|
|
||||||
"label.roomName": "Room name",
|
"label.roomName": null,
|
||||||
"label.chooseRoomButton": "Continue",
|
"label.chooseRoomButton": null,
|
||||||
"label.yourName": "Dein Name",
|
"label.yourName": "Dein Name",
|
||||||
"label.newWindow": "In separatem Fenster öffnen",
|
"label.newWindow": "In separatem Fenster öffnen",
|
||||||
"label.fullscreen": "Vollbild",
|
"label.fullscreen": "Vollbild",
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"room.spotlights": "Deltagere i fokus",
|
"room.spotlights": "Deltagere i fokus",
|
||||||
"room.passive": "Passive deltagere",
|
"room.passive": "Passive deltagere",
|
||||||
"room.videoPaused": "Denne video er sat på pause",
|
"room.videoPaused": "Denne video er sat på pause",
|
||||||
|
"room.muteAll": null,
|
||||||
|
"room.stopAllVideo": null,
|
||||||
|
"room.closeMeeting": null,
|
||||||
|
|
||||||
"tooltip.login": "Log ind",
|
"tooltip.login": "Log ind",
|
||||||
"tooltip.logout": "Log ud",
|
"tooltip.logout": "Log ud",
|
||||||
|
|
@ -60,6 +63,7 @@
|
||||||
"tooltip.lobby": "Vis lobby",
|
"tooltip.lobby": "Vis lobby",
|
||||||
"tooltip.settings": "Vis indstillinger",
|
"tooltip.settings": "Vis indstillinger",
|
||||||
"tooltip.participants": "Vis deltagere",
|
"tooltip.participants": "Vis deltagere",
|
||||||
|
"tooltip.kickParticipant": null,
|
||||||
|
|
||||||
"label.roomName": "Værelsesnavn",
|
"label.roomName": "Værelsesnavn",
|
||||||
"label.chooseRoomButton": "Fortsæt",
|
"label.chooseRoomButton": "Fortsæt",
|
||||||
|
|
@ -106,7 +110,7 @@
|
||||||
"filesharing.finished": "Filen er færdig med at downloade",
|
"filesharing.finished": "Filen er færdig med at downloade",
|
||||||
"filesharing.save": "Gem",
|
"filesharing.save": "Gem",
|
||||||
"filesharing.sharedFile": "{displayName} delte en fil",
|
"filesharing.sharedFile": "{displayName} delte en fil",
|
||||||
"filesharing.download": "Download",
|
"filesharing.download": null,
|
||||||
"filesharing.missingSeeds": "Hvis denne proces tager lang tid, er der muligvis ikke nogen, der seedede denne torrent. Prøv at bede nogen om at uploade den fil, du ønsker at hente.",
|
"filesharing.missingSeeds": "Hvis denne proces tager lang tid, er der muligvis ikke nogen, der seedede denne torrent. Prøv at bede nogen om at uploade den fil, du ønsker at hente.",
|
||||||
|
|
||||||
"device.devicesChanged": "Detekteret ndringer i dine enheder, konfigurer dine enheder i indstillingsdialogen",
|
"device.devicesChanged": "Detekteret ndringer i dine enheder, konfigurer dine enheder i indstillingsdialogen",
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"room.spotlights": "Συμμετέχοντες στο Spotlight",
|
"room.spotlights": "Συμμετέχοντες στο Spotlight",
|
||||||
"room.passive": "Παθητικοί συμμετέχοντες",
|
"room.passive": "Παθητικοί συμμετέχοντες",
|
||||||
"room.videoPaused": "Το βίντεο έχει σταματήσει",
|
"room.videoPaused": "Το βίντεο έχει σταματήσει",
|
||||||
|
"room.muteAll": null,
|
||||||
|
"room.stopAllVideo": null,
|
||||||
|
"room.closeMeeting": null,
|
||||||
|
|
||||||
"tooltip.login": "Σύνδεση",
|
"tooltip.login": "Σύνδεση",
|
||||||
"tooltip.logout": "Αποσύνδεση",
|
"tooltip.logout": "Αποσύνδεση",
|
||||||
|
|
@ -60,6 +63,7 @@
|
||||||
"tooltip.lobby": "Εμφάνιση λόμπι",
|
"tooltip.lobby": "Εμφάνιση λόμπι",
|
||||||
"tooltip.settings": "Εμφάνιση ρυθμίσεων",
|
"tooltip.settings": "Εμφάνιση ρυθμίσεων",
|
||||||
"tooltip.participants": "Εμφάνιση συμμετεχόντων",
|
"tooltip.participants": "Εμφάνιση συμμετεχόντων",
|
||||||
|
"tooltip.kickParticipant": null,
|
||||||
|
|
||||||
"label.roomName": "Όνομα δωματίου",
|
"label.roomName": "Όνομα δωματίου",
|
||||||
"label.chooseRoomButton": "Συνέχεια",
|
"label.chooseRoomButton": "Συνέχεια",
|
||||||
|
|
@ -75,8 +79,8 @@
|
||||||
"label.shareFile": "Διαμοιραστείτε ένα αρχείο",
|
"label.shareFile": "Διαμοιραστείτε ένα αρχείο",
|
||||||
"label.fileSharingUnsupported": "Ο διαμοιρασμός αρχείων δεν υποστηρίζεται",
|
"label.fileSharingUnsupported": "Ο διαμοιρασμός αρχείων δεν υποστηρίζεται",
|
||||||
"label.unknown": "Άγνωστο",
|
"label.unknown": "Άγνωστο",
|
||||||
"label.democratic": "Democratic view",
|
"label.democratic": null,
|
||||||
"label.filmstrip": "Filmstrip view",
|
"label.filmstrip": null,
|
||||||
"label.low": "Χαμηλή",
|
"label.low": "Χαμηλή",
|
||||||
"label.medium": "Μέτρια",
|
"label.medium": "Μέτρια",
|
||||||
"label.high": "Υψηλή (HD)",
|
"label.high": "Υψηλή (HD)",
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"room.spotlights": "Participants in Spotlight",
|
"room.spotlights": "Participants in Spotlight",
|
||||||
"room.passive": "Passive Participants",
|
"room.passive": "Passive Participants",
|
||||||
"room.videoPaused": "This video is paused",
|
"room.videoPaused": "This video is paused",
|
||||||
|
"room.muteAll": "Mute all",
|
||||||
|
"room.stopAllVideo": "Stop all video",
|
||||||
|
"room.closeMeeting": "Close meeting",
|
||||||
|
|
||||||
"tooltip.login": "Log in",
|
"tooltip.login": "Log in",
|
||||||
"tooltip.logout": "Log out",
|
"tooltip.logout": "Log out",
|
||||||
|
|
@ -60,6 +63,7 @@
|
||||||
"tooltip.lobby": "Show lobby",
|
"tooltip.lobby": "Show lobby",
|
||||||
"tooltip.settings": "Show settings",
|
"tooltip.settings": "Show settings",
|
||||||
"tooltip.participants": "Show participants",
|
"tooltip.participants": "Show participants",
|
||||||
|
"tooltip.kickParticipant": "Kick out participant",
|
||||||
|
|
||||||
"label.roomName": "Room name",
|
"label.roomName": "Room name",
|
||||||
"label.chooseRoomButton": "Continue",
|
"label.chooseRoomButton": "Continue",
|
||||||
|
|
|
||||||
|
|
@ -1,140 +1,144 @@
|
||||||
{
|
{
|
||||||
"socket.disconnected": "Desconectado",
|
"socket.disconnected": "Desconectado",
|
||||||
"socket.reconnecting": "Desconectado, intentando reconectar",
|
"socket.reconnecting": "Desconectado, intentando reconectar",
|
||||||
"socket.reconnected": "Reconectado",
|
"socket.reconnected": "Reconectado",
|
||||||
"socket.requestError": "Error en la petición al servidor",
|
"socket.requestError": "Error en la petición al servidor",
|
||||||
|
|
||||||
"room.chooseRoom": "Indique el nombre de la sala a la que le gustaría unirse",
|
"room.chooseRoom": "Indique el nombre de la sala a la que le gustaría unirse",
|
||||||
"room.cookieConsent": "Esta web utiliza cookies para mejorar la experiencia de usuario",
|
"room.cookieConsent": "Esta web utiliza cookies para mejorar la experiencia de usuario",
|
||||||
"room.consentUnderstand": "I understand",
|
"room.consentUnderstand": "I understand",
|
||||||
"room.joined": "Se ha unido a la sala",
|
"room.joined": "Se ha unido a la sala",
|
||||||
"room.cantJoin": "No ha sido posible unirse a la sala",
|
"room.cantJoin": "No ha sido posible unirse a la sala",
|
||||||
"room.youLocked": "Ha cerrado la sala",
|
"room.youLocked": "Ha cerrado la sala",
|
||||||
"room.cantLock": "No ha sido posible cerrar la sala",
|
"room.cantLock": "No ha sido posible cerrar la sala",
|
||||||
"room.youUnLocked": "Ha abierto la sala",
|
"room.youUnLocked": "Ha abierto la sala",
|
||||||
"room.cantUnLock": "No ha sido posible abrir la sala",
|
"room.cantUnLock": "No ha sido posible abrir la sala",
|
||||||
"room.locked": "La sala ahora es privada",
|
"room.locked": "La sala ahora es privada",
|
||||||
"room.unlocked": "La sala ahora es pública",
|
"room.unlocked": "La sala ahora es pública",
|
||||||
"room.newLobbyPeer": "Nuevo participante en la sala de espera",
|
"room.newLobbyPeer": "Nuevo participante en la sala de espera",
|
||||||
"room.lobbyPeerLeft": "Un participante en espera ha salido",
|
"room.lobbyPeerLeft": "Un participante en espera ha salido",
|
||||||
"room.lobbyPeerChangedDisplayName": "Participante en espera cambió su nombre a {displayName}",
|
"room.lobbyPeerChangedDisplayName": "Participante en espera cambió su nombre a {displayName}",
|
||||||
"room.lobbyPeerChangedPicture": "Participante en espera cambió su foto",
|
"room.lobbyPeerChangedPicture": "Participante en espera cambió su foto",
|
||||||
"room.setAccessCode": "Código de acceso de la sala actualizado",
|
"room.setAccessCode": "Código de acceso de la sala actualizado",
|
||||||
"room.accessCodeOn": "Código de acceso de la sala activado",
|
"room.accessCodeOn": "Código de acceso de la sala activado",
|
||||||
"room.accessCodeOff": "Código de acceso de la sala desactivado",
|
"room.accessCodeOff": "Código de acceso de la sala desactivado",
|
||||||
"room.peerChangedDisplayName": "{oldDisplayName} es ahora {displayName}",
|
"room.peerChangedDisplayName": "{oldDisplayName} es ahora {displayName}",
|
||||||
"room.newPeer": "{displayName} se unió a la sala",
|
"room.newPeer": "{displayName} se unió a la sala",
|
||||||
"room.newFile": "Nuevo fichero disponible",
|
"room.newFile": "Nuevo fichero disponible",
|
||||||
"room.toggleAdvancedMode": "Cambiado a modo avanzado",
|
"room.toggleAdvancedMode": "Cambiado a modo avanzado",
|
||||||
"room.setDemocraticView": "Cambiado a modo democrático",
|
"room.setDemocraticView": "Cambiado a modo democrático",
|
||||||
"room.setFilmStripView": "Cambiado a modo viñeta",
|
"room.setFilmStripView": "Cambiado a modo viñeta",
|
||||||
"room.loggedIn": "Ha iniciado sesión",
|
"room.loggedIn": "Ha iniciado sesión",
|
||||||
"room.loggedOut": "Ha cerrado su sesión",
|
"room.loggedOut": "Ha cerrado su sesión",
|
||||||
"room.changedDisplayName": "Ha cambiado su nombre a {displayName}",
|
"room.changedDisplayName": "Ha cambiado su nombre a {displayName}",
|
||||||
"room.changeDisplayNameError": "Hubo un error al intentar cambiar su nombre",
|
"room.changeDisplayNameError": "Hubo un error al intentar cambiar su nombre",
|
||||||
"room.chatError": "No ha sido posible enviar su mensaje",
|
"room.chatError": "No ha sido posible enviar su mensaje",
|
||||||
"room.aboutToJoin": "Está a punto de unirse a una reunión",
|
"room.aboutToJoin": "Está a punto de unirse a una reunión",
|
||||||
"room.roomId": "ID de la sala: {roomName}",
|
"room.roomId": "ID de la sala: {roomName}",
|
||||||
"room.setYourName": "Indique el nombre con el que quiere participar y cómo quiere unirse:",
|
"room.setYourName": "Indique el nombre con el que quiere participar y cómo quiere unirse:",
|
||||||
"room.audioOnly": "Solo sonido",
|
"room.audioOnly": "Solo sonido",
|
||||||
"room.audioVideo": "Sonido y vídeo",
|
"room.audioVideo": "Sonido y vídeo",
|
||||||
"room.youAreReady": "Ok, está preparado",
|
"room.youAreReady": "Ok, está preparado",
|
||||||
"room.emptyRequireLogin": "¡La sala está vacía! Puede iniciar sesión para comenzar la reunión o esperar hasta que el anfitrión se una",
|
"room.emptyRequireLogin": "¡La sala está vacía! Puede iniciar sesión para comenzar la reunión o esperar hasta que el anfitrión se una",
|
||||||
"room.locketWait": "La sala es privada - espere hasta que alguien le invite ...",
|
"room.locketWait": "La sala es privada - espere hasta que alguien le invite ...",
|
||||||
"room.lobbyAdministration": "Administración de la sala de espera",
|
"room.lobbyAdministration": "Administración de la sala de espera",
|
||||||
"room.peersInLobby": "Participantes en la sala de espera",
|
"room.peersInLobby": "Participantes en la sala de espera",
|
||||||
"room.lobbyEmpty": "La sala de espera está vacía",
|
"room.lobbyEmpty": "La sala de espera está vacía",
|
||||||
"room.hiddenPeers": "{hiddenPeersCount, plural, one {participante} other {participantes}}",
|
"room.hiddenPeers": "{hiddenPeersCount, plural, one {participante} other {participantes}}",
|
||||||
"room.me": "Yo",
|
"room.me": "Yo",
|
||||||
"room.spotlights": "Participantes destacados",
|
"room.spotlights": "Participantes destacados",
|
||||||
"room.passive": "Participantes pasivos",
|
"room.passive": "Participantes pasivos",
|
||||||
"room.videoPaused": "El vídeo está pausado",
|
"room.videoPaused": "El vídeo está pausado",
|
||||||
|
"room.muteAll": null,
|
||||||
|
"room.stopAllVideo": null,
|
||||||
|
"room.closeMeeting": null,
|
||||||
|
|
||||||
"tooltip.login": "Entrar",
|
"tooltip.login": "Entrar",
|
||||||
"tooltip.logout": "Salir",
|
"tooltip.logout": "Salir",
|
||||||
"tooltip.admitFromLobby": "Admitir desde la sala de espera",
|
"tooltip.admitFromLobby": "Admitir desde la sala de espera",
|
||||||
"tooltip.lockRoom": "Configurar sala como privada",
|
"tooltip.lockRoom": "Configurar sala como privada",
|
||||||
"tooltip.unLockRoom": "Configurar sala como pública",
|
"tooltip.unLockRoom": "Configurar sala como pública",
|
||||||
"tooltip.enterFullscreen": "Presentar en pantalla completa",
|
"tooltip.enterFullscreen": "Presentar en pantalla completa",
|
||||||
"tooltip.leaveFullscreen": "Salir de la pantalla completa",
|
"tooltip.leaveFullscreen": "Salir de la pantalla completa",
|
||||||
"tooltip.lobby": "Mostrar sala de espera",
|
"tooltip.lobby": "Mostrar sala de espera",
|
||||||
"tooltip.settings": "Mostrar ajustes",
|
"tooltip.settings": "Mostrar ajustes",
|
||||||
"tooltip.participants": "Mostrar participantes",
|
"tooltip.participants": "Mostrar participantes",
|
||||||
|
"tooltip.kickParticipant": null,
|
||||||
|
|
||||||
"label.roomName": "Nombre de la sala",
|
"label.roomName": "Nombre de la sala",
|
||||||
"label.chooseRoomButton": "Continuar",
|
"label.chooseRoomButton": "Continuar",
|
||||||
"label.yourName": "Su nombre",
|
"label.yourName": "Su nombre",
|
||||||
"label.newWindow": "Nueva ventana",
|
"label.newWindow": "Nueva ventana",
|
||||||
"label.fullscreen": "Pantalla completa",
|
"label.fullscreen": "Pantalla completa",
|
||||||
"label.openDrawer": "Abrir panel",
|
"label.openDrawer": "Abrir panel",
|
||||||
"label.leave": "Salir",
|
"label.leave": "Salir",
|
||||||
"label.chatInput": "Escriba su mensaje...",
|
"label.chatInput": "Escriba su mensaje...",
|
||||||
"label.chat": "Chat",
|
"label.chat": "Chat",
|
||||||
"label.filesharing": "Compartir ficheros",
|
"label.filesharing": "Compartir ficheros",
|
||||||
"label.participants": "Participantes",
|
"label.participants": "Participantes",
|
||||||
"label.shareFile": "Compartir fichero",
|
"label.shareFile": "Compartir fichero",
|
||||||
"label.fileSharingUnsupported": "Compartir ficheros no está disponible",
|
"label.fileSharingUnsupported": "Compartir ficheros no está disponible",
|
||||||
"label.unknown": "Desconocido",
|
"label.unknown": "Desconocido",
|
||||||
"label.democratic": "Vista democrática",
|
"label.democratic": "Vista democrática",
|
||||||
"label.filmstrip": "Vista en viñeta",
|
"label.filmstrip": "Vista en viñeta",
|
||||||
"label.low": "Baja",
|
"label.low": "Baja",
|
||||||
"label.medium": "Media",
|
"label.medium": "Media",
|
||||||
"label.high": "Alta (HD)",
|
"label.high": "Alta (HD)",
|
||||||
"label.veryHigh": "Muy alta (FHD)",
|
"label.veryHigh": "Muy alta (FHD)",
|
||||||
"label.ultra": "Ultra (UHD)",
|
"label.ultra": "Ultra (UHD)",
|
||||||
"label.close": "Cerrar",
|
"label.close": "Cerrar",
|
||||||
|
|
||||||
"settings.settings": "Ajustes",
|
"settings.settings": "Ajustes",
|
||||||
"settings.camera": "Cámara",
|
"settings.camera": "Cámara",
|
||||||
"settings.selectCamera": "Seleccionar dispositivo de vídeo",
|
"settings.selectCamera": "Seleccionar dispositivo de vídeo",
|
||||||
"settings.cantSelectCamera": "No ha sido posible seleccionar el dispositivo de vídeo",
|
"settings.cantSelectCamera": "No ha sido posible seleccionar el dispositivo de vídeo",
|
||||||
"settings.audio": "Dispositivo de sonido",
|
"settings.audio": "Dispositivo de sonido",
|
||||||
"settings.selectAudio": "Seleccione dispositivo de sonido",
|
"settings.selectAudio": "Seleccione dispositivo de sonido",
|
||||||
"settings.cantSelectAudio": "No ha sido posible seleccionar el dispositivo de sonido",
|
"settings.cantSelectAudio": "No ha sido posible seleccionar el dispositivo de sonido",
|
||||||
"settings.resolution": "Seleccione su resolución de imagen",
|
"settings.resolution": "Seleccione su resolución de imagen",
|
||||||
"settings.layout": "Disposición de la sala",
|
"settings.layout": "Disposición de la sala",
|
||||||
"settings.selectRoomLayout": "Seleccione la disposición de la sala",
|
"settings.selectRoomLayout": "Seleccione la disposición de la sala",
|
||||||
"settings.advancedMode": "Modo avanzado",
|
"settings.advancedMode": "Modo avanzado",
|
||||||
"settings.permanentTopBar": "Barra superior permanente",
|
"settings.permanentTopBar": "Barra superior permanente",
|
||||||
"settings.lastn": "Cantidad de videos visibles",
|
"settings.lastn": "Cantidad de videos visibles",
|
||||||
|
|
||||||
"filesharing.saveFileError": "No ha sido posible guardar el fichero",
|
"filesharing.saveFileError": "No ha sido posible guardar el fichero",
|
||||||
"filesharing.startingFileShare": "Intentando compartir el fichero",
|
"filesharing.startingFileShare": "Intentando compartir el fichero",
|
||||||
"filesharing.successfulFileShare": "El fichero se compartió con éxito",
|
"filesharing.successfulFileShare": "El fichero se compartió con éxito",
|
||||||
"filesharing.unableToShare": "No ha sido posible compartir el fichero",
|
"filesharing.unableToShare": "No ha sido posible compartir el fichero",
|
||||||
"filesharing.error": "Hubo un error al compartir el fichero",
|
"filesharing.error": "Hubo un error al compartir el fichero",
|
||||||
"filesharing.finished": "Descarga del fichero finalizada",
|
"filesharing.finished": "Descarga del fichero finalizada",
|
||||||
"filesharing.save": "Guardar",
|
"filesharing.save": "Guardar",
|
||||||
"filesharing.sharedFile": "{displayName} compartió un fichero",
|
"filesharing.sharedFile": "{displayName} compartió un fichero",
|
||||||
"filesharing.download": "Descargar",
|
"filesharing.download": "Descargar",
|
||||||
"filesharing.missingSeeds": "Si este proceso demora en exceso, puede ocurrir que no haya nadie compartiendo el fichero. Pruebe a pedirle a alguien que vuelva a subir el fichero que busca.",
|
"filesharing.missingSeeds": "Si este proceso demora en exceso, puede ocurrir que no haya nadie compartiendo el fichero. Pruebe a pedirle a alguien que vuelva a subir el fichero que busca.",
|
||||||
|
|
||||||
"devices.devicesChanged": "Sus dispositivos han cambiado, vuelva a configurarlos en la ventana de ajustes",
|
"devices.devicesChanged": "Sus dispositivos han cambiado, vuelva a configurarlos en la ventana de ajustes",
|
||||||
|
|
||||||
"device.audioUnsupported": "Sonido no disponible",
|
"device.audioUnsupported": "Sonido no disponible",
|
||||||
"device.activateAudio": "Activar sonido",
|
"device.activateAudio": "Activar sonido",
|
||||||
"device.muteAudio": "Silenciar sonido",
|
"device.muteAudio": "Silenciar sonido",
|
||||||
"device.unMuteAudio": "Reactivar sonido",
|
"device.unMuteAudio": "Reactivar sonido",
|
||||||
|
|
||||||
"device.videoUnsupported": "Vídeo no disponible",
|
"device.videoUnsupported": "Vídeo no disponible",
|
||||||
"device.startVideo": "Iniciar vídeo",
|
"device.startVideo": "Iniciar vídeo",
|
||||||
"device.stopVideo": "Detener vídeo",
|
"device.stopVideo": "Detener vídeo",
|
||||||
|
|
||||||
"device.screenSharingUnsupported": "Compartir pantalla no disponible",
|
"device.screenSharingUnsupported": "Compartir pantalla no disponible",
|
||||||
"device.startScreenSharing": "Iniciar compartir pantalla",
|
"device.startScreenSharing": "Iniciar compartir pantalla",
|
||||||
"device.stopScreenSharing": "Detener compartir pantalla",
|
"device.stopScreenSharing": "Detener compartir pantalla",
|
||||||
|
|
||||||
"devices.microphoneDisconnected": "Micrófono desconectado",
|
"devices.microphoneDisconnected": "Micrófono desconectado",
|
||||||
"devices.microphoneError": "Hubo un error al acceder a su micrófono",
|
"devices.microphoneError": "Hubo un error al acceder a su micrófono",
|
||||||
"devices.microPhoneMute": "Desactivar micrófono",
|
"devices.microPhoneMute": "Desactivar micrófono",
|
||||||
"devices.micophoneUnMute": "Activar micrófono",
|
"devices.micophoneUnMute": "Activar micrófono",
|
||||||
"devices.microphoneEnable": "Micrófono activado",
|
"devices.microphoneEnable": "Micrófono activado",
|
||||||
"devices.microphoneMuteError": "No ha sido posible desactivar su micrófono",
|
"devices.microphoneMuteError": "No ha sido posible desactivar su micrófono",
|
||||||
"devices.microphoneUnMuteError": "No ha sido posible activar su micrófono",
|
"devices.microphoneUnMuteError": "No ha sido posible activar su micrófono",
|
||||||
|
|
||||||
"devices.screenSharingDisconnected": "Pantalla compartida desconectada",
|
"devices.screenSharingDisconnected": "Pantalla compartida desconectada",
|
||||||
"devices.screenSharingError": "Hubo un error al acceder a su pantalla",
|
"devices.screenSharingError": "Hubo un error al acceder a su pantalla",
|
||||||
|
|
||||||
"devices.cameraDisconnected": "Cámara desconectada",
|
"devices.cameraDisconnected": "Cámara desconectada",
|
||||||
"devices.cameraError": "Hubo un error al acceder a su cámara"
|
"devices.cameraError": "Hubo un error al acceder a su cámara"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,139 +1,143 @@
|
||||||
{
|
{
|
||||||
"socket.disconnected" : " Vous avez été déconnecté",
|
"socket.disconnected": "Vous avez été déconnecté",
|
||||||
"socket.reconnecting" : " Vous avez été déconnecté, reconnexion en cours",
|
"socket.reconnecting": "Vous avez été déconnecté, reconnexion en cours",
|
||||||
"socket.reconnected" : " Vous êtes reconnecté",
|
"socket.reconnected": "Vous êtes reconnecté",
|
||||||
"socket.requestError" : " Erreur sur une requête serveur",
|
"socket.requestError": "Erreur sur une requête serveur",
|
||||||
|
|
||||||
"room.chooseRoom" : " Choisissez le nom de la réunion que vous souhaitez rejoindre",
|
"room.chooseRoom": "Choisissez le nom de la réunion que vous souhaitez rejoindre",
|
||||||
"room.cookieConsent" : " Ce site utilise les cookies pour améliorer votre expérience utilisateur",
|
"room.cookieConsent": "Ce site utilise les cookies pour améliorer votre expérience utilisateur",
|
||||||
"room.consentUnderstand": "I understand",
|
"room.consentUnderstand": "I understand",
|
||||||
"room.joined" : " Vous avez rejoint la salle",
|
"room.joined": "Vous avez rejoint la salle",
|
||||||
"room.cantJoin" : " Impossible de rejoindre la salle",
|
"room.cantJoin": "Impossible de rejoindre la salle",
|
||||||
"room.youLocked" : " Vous avez privatisé la salle",
|
"room.youLocked": "Vous avez privatisé la salle",
|
||||||
"room.cantLock" : " Impossible de privatiser la salle",
|
"room.cantLock": "Impossible de privatiser la salle",
|
||||||
"room.youUnLocked" : " Vous avez dé-privatiser la salle",
|
"room.youUnLocked": "Vous avez dé-privatiser la salle",
|
||||||
"room.cantUnLock" : " Impossible de dé-privatiser la réunion",
|
"room.cantUnLock": "Impossible de dé-privatiser la réunion",
|
||||||
"room.locked" : " La réunion est privée",
|
"room.locked": "La réunion est privée",
|
||||||
"room.unlocked" : " La réunion est publique",
|
"room.unlocked": "La réunion est publique",
|
||||||
"room.newLobbyPeer" : " Un nouveau participant est dans la salle d’attente",
|
"room.newLobbyPeer": "Un nouveau participant est dans la salle d’attente",
|
||||||
"room.lobbyPeerLeft" : " Un participant de la salle d’attente vient de partir",
|
"room.lobbyPeerLeft": "Un participant de la salle d’attente vient de partir",
|
||||||
"room.lobbyPeerChangedDisplayName" : " Un participant dans la salle d’attente a changé de nom pour {displayName}",
|
"room.lobbyPeerChangedDisplayName": "Un participant dans la salle d’attente a changé de nom pour {displayName}",
|
||||||
"room.lobbyPeerChangedPicture" : " Un participant dans le hall à changer de photo",
|
"room.lobbyPeerChangedPicture": "Un participant dans le hall à changer de photo",
|
||||||
"room.setAccessCode" : " Code d’accès à la réunion mis à jour",
|
"room.setAccessCode": "Code d’accès à la réunion mis à jour",
|
||||||
"room.accessCodeOn" : " Code d’accès à la réunion activée",
|
"room.accessCodeOn": "Code d’accès à la réunion activée",
|
||||||
"room.accessCodeOff" : " Code d’accès à la réunion désactivée",
|
"room.accessCodeOff": "Code d’accès à la réunion désactivée",
|
||||||
"room.peerChangedDisplayName" : " {oldDisplayName} est maintenant {displayName}",
|
"room.peerChangedDisplayName": "{oldDisplayName} est maintenant {displayName}",
|
||||||
"room.newPeer" : " {displayName} a rejoint la salle",
|
"room.newPeer": "{displayName} a rejoint la salle",
|
||||||
"room.newFile" : " Nouveau fichier disponible",
|
"room.newFile": "Nouveau fichier disponible",
|
||||||
"room.toggleAdvancedMode" : " Basculer en mode avancé",
|
"room.toggleAdvancedMode": "Basculer en mode avancé",
|
||||||
"room.setDemocraticView" : " Passer en vue démocratique",
|
"room.setDemocraticView": "Passer en vue démocratique",
|
||||||
"room.setFilmStripView" : " Passer en vue vignette",
|
"room.setFilmStripView": "Passer en vue vignette",
|
||||||
"room.loggedIn" : " Vous êtes connecté",
|
"room.loggedIn": "Vous êtes connecté",
|
||||||
"room.loggedOut" : " Vous êtes déconnecté",
|
"room.loggedOut": "Vous êtes déconnecté",
|
||||||
"room.changedDisplayName" : " Votre nom à changer pour {displayname}",
|
"room.changedDisplayName": "Votre nom à changer pour {displayname}",
|
||||||
"room.changeDisplayNameError" : " Une erreur s’est produite pour votre changement de nom",
|
"room.changeDisplayNameError": "Une erreur s’est produite pour votre changement de nom",
|
||||||
"room.chatError" : " Impossible d’envoyer un message",
|
"room.chatError": "Impossible d’envoyer un message",
|
||||||
"room.aboutToJoin" : " Vous allez rejoindre une réunion",
|
"room.aboutToJoin": "Vous allez rejoindre une réunion",
|
||||||
"room.roomId" : " Salle ID: {roomName}",
|
"room.roomId": "Salle ID: {roomName}",
|
||||||
"room.setYourName" : " Choisissez votre nom de participant puis comment vous connecter :",
|
"room.setYourName": "Choisissez votre nom de participant puis comment vous connecter:",
|
||||||
"room.audioOnly" : " Audio uniquement",
|
"room.audioOnly": "Audio uniquement",
|
||||||
"room.audioVideo" : " Audio et Vidéo",
|
"room.audioVideo": "Audio et Vidéo",
|
||||||
"room.youAreReady" : " Ok, vous êtes prêt",
|
"room.youAreReady": "Ok, vous êtes prêt",
|
||||||
"room.emptyRequireLogin" : " La réunion est vide ! Vous pouvez vous connecter pour commencer la réunion ou attendre qu'un hôte se connecte",
|
"room.emptyRequireLogin": "La réunion est vide ! Vous pouvez vous connecter pour commencer la réunion ou attendre qu'un hôte se connecte",
|
||||||
"room.locketWait" : " La réunion est privatisée - attendez que quelqu’un vous laisse entrer",
|
"room.locketWait": "La réunion est privatisée - attendez que quelqu’un vous laisse entrer",
|
||||||
"room.lobbyAdministration" : " Administration de la salle d’attente",
|
"room.lobbyAdministration": "Administration de la salle d’attente",
|
||||||
"room.peersInLobby" : " Participants dans la salle d’attente",
|
"room.peersInLobby": "Participants dans la salle d’attente",
|
||||||
"room.lobbyEmpty" : " Il n'y a actuellement aucun participant dans la salle d'attente",
|
"room.lobbyEmpty": "Il n'y a actuellement aucun participant dans la salle d'attente",
|
||||||
"room.hiddenPeers" : " {hiddenPeersCount, plural, one {participant} other {participants}}",
|
"room.hiddenPeers": "{hiddenPeersCount, plural, one {participant} other {participants}}",
|
||||||
"room.me" : " Moi",
|
"room.me": "Moi",
|
||||||
"room.spotlights" : " Participants actifs",
|
"room.spotlights": "Participants actifs",
|
||||||
"room.passive" : " Participants passifs",
|
"room.passive": "Participants passifs",
|
||||||
"room.videoPaused" : " La vidéo est en pause",
|
"room.videoPaused": "La vidéo est en pause",
|
||||||
|
"room.muteAll": null,
|
||||||
|
"room.stopAllVideo": null,
|
||||||
|
"room.closeMeeting": null,
|
||||||
|
|
||||||
"tooltip.login" : " Connexion",
|
"tooltip.login": "Connexion",
|
||||||
"tooltip.logout" : " Déconnexion",
|
"tooltip.logout": "Déconnexion",
|
||||||
"tooltip.admitFromLobby" : " Autorisé depuis la salle d'attente",
|
"tooltip.admitFromLobby": "Autorisé depuis la salle d'attente",
|
||||||
"tooltip.lockRoom" : " Privatisation de la salle",
|
"tooltip.lockRoom": "Privatisation de la salle",
|
||||||
"tooltip.unLockRoom" : " Dé-privatisation de la salle",
|
"tooltip.unLockRoom": "Dé-privatisation de la salle",
|
||||||
"tooltip.enterFullscreen" : " Afficher en plein écran",
|
"tooltip.enterFullscreen": "Afficher en plein écran",
|
||||||
"tooltip.leaveFullscreen" : " Quitter le plein écran",
|
"tooltip.leaveFullscreen": "Quitter le plein écran",
|
||||||
"tooltip.lobby" : " Afficher la salle d'attente",
|
"tooltip.lobby": "Afficher la salle d'attente",
|
||||||
"tooltip.settings" : " Afficher les paramètres",
|
"tooltip.settings": "Afficher les paramètres",
|
||||||
"tooltip.participants": "Afficher les participants",
|
"tooltip.participants": "Afficher les participants",
|
||||||
|
"tooltip.kickParticipant": null,
|
||||||
|
|
||||||
"label.roomName" : " Nom de la salle",
|
"label.roomName": "Nom de la salle",
|
||||||
"label.chooseRoomButton" : " Continuer",
|
"label.chooseRoomButton": "Continuer",
|
||||||
"label.yourName" : " Votre nom",
|
"label.yourName": "Votre nom",
|
||||||
"label.newWindow" : " Nouvelle fenêtre",
|
"label.newWindow": "Nouvelle fenêtre",
|
||||||
"label.fullscreen" : " Plein écran",
|
"label.fullscreen": "Plein écran",
|
||||||
"label.openDrawer" : " Ouvrir Drawer",
|
"label.openDrawer": "Ouvrir Drawer",
|
||||||
"label.leave" : " Quiter",
|
"label.leave": "Quiter",
|
||||||
"label.chatInput" : " Entrer un message",
|
"label.chatInput": "Entrer un message",
|
||||||
"label.chat" : " Chat",
|
"label.chat": "Chat",
|
||||||
"label.filesharing" : " Partage de fichier",
|
"label.filesharing": "Partage de fichier",
|
||||||
"label.participants" : " Participants",
|
"label.participants": "Participants",
|
||||||
"label.shareFile" : " Partager un fichier",
|
"label.shareFile": "Partager un fichier",
|
||||||
"label.fileSharingUnsupported" : " Partage de fichier non supporté",
|
"label.fileSharingUnsupported": "Partage de fichier non supporté",
|
||||||
"label.unknown" : " Inconnu",
|
"label.unknown": "Inconnu",
|
||||||
"label.democratic" : " Vue démocratique",
|
"label.democratic": "Vue démocratique",
|
||||||
"label.filmstrip" : " Vue avec miniature",
|
"label.filmstrip": "Vue avec miniature",
|
||||||
"label.low" : " Basse définition",
|
"label.low": "Basse définition",
|
||||||
"label.medium" : " Définition normale",
|
"label.medium": "Définition normale",
|
||||||
"label.high" : " Haute Définition (HD)",
|
"label.high": "Haute Définition (HD)",
|
||||||
"label.veryHigh" : " Très Haute Définition (FHD)",
|
"label.veryHigh": "Très Haute Définition (FHD)",
|
||||||
"label.ultra" : " Ultra Haute Définition",
|
"label.ultra": "Ultra Haute Définition",
|
||||||
"label.close" : " Fermer",
|
"label.close": "Fermer",
|
||||||
|
|
||||||
"settings.settings" : " Paramètres",
|
"settings.settings": "Paramètres",
|
||||||
"settings.camera" : " Caméra",
|
"settings.camera": "Caméra",
|
||||||
"settings.selectCamera" : " Sélectionner votre caméra",
|
"settings.selectCamera": "Sélectionner votre caméra",
|
||||||
"settings.cantSelectCamera" : " Impossible de sélectionner votre caméra",
|
"settings.cantSelectCamera": "Impossible de sélectionner votre caméra",
|
||||||
"settings.audio" : " Microphone",
|
"settings.audio": "Microphone",
|
||||||
"settings.selectAudio" : " Sélectionner votre microphone",
|
"settings.selectAudio": "Sélectionner votre microphone",
|
||||||
"settings.cantSelectAudio" : " Impossible de sélectionner votre la caméra",
|
"settings.cantSelectAudio": "Impossible de sélectionner votre la caméra",
|
||||||
"settings.resolution" : " Sélection votre résolution",
|
"settings.resolution": "Sélection votre résolution",
|
||||||
"settings.layout" : " Mode d'affichage de la salle",
|
"settings.layout": "Mode d'affichage de la salle",
|
||||||
"settings.selectRoomLayout" : " Sélectionner l'affiche de la salle",
|
"settings.selectRoomLayout": "Sélectionner l'affiche de la salle",
|
||||||
"settings.advancedMode" : " Mode avancé",
|
"settings.advancedMode": "Mode avancé",
|
||||||
"settings.permanentTopBar": "Barre supérieure permanente",
|
"settings.permanentTopBar": "Barre supérieure permanente",
|
||||||
"settings.lastn": "Nombre de vidéos visibles",
|
"settings.lastn": "Nombre de vidéos visibles",
|
||||||
|
|
||||||
"filesharing.saveFileError" : " Impossible d'enregistrer le fichier",
|
"filesharing.saveFileError": "Impossible d'enregistrer le fichier",
|
||||||
"filesharing.startingFileShare" : " Début du transfert de fichier",
|
"filesharing.startingFileShare": "Début du transfert de fichier",
|
||||||
"filesharing.successfulFileShare" : " Fichier transféré",
|
"filesharing.successfulFileShare": "Fichier transféré",
|
||||||
"filesharing.unableToShare" : " Impossible de transférer le fichier",
|
"filesharing.unableToShare": "Impossible de transférer le fichier",
|
||||||
"filesharing.error" : " Erreur lors du transfert de fichier",
|
"filesharing.error": "Erreur lors du transfert de fichier",
|
||||||
"filesharing.finished" : " Fin du transfert de fichier",
|
"filesharing.finished": "Fin du transfert de fichier",
|
||||||
"filesharing.save" : " Sauver",
|
"filesharing.save": "Sauver",
|
||||||
"filesharing.sharedFile" : " {displayName} a partagé un fichier",
|
"filesharing.sharedFile": "{displayName} a partagé un fichier",
|
||||||
"filesharing.download" : " Télécharger",
|
"filesharing.download": "Télécharger",
|
||||||
"filesharing.missingSeeds" : " Si le téléchargement prend trop de temps c’est qu’il n’y a peut-être plus personne qui partage ce torrent. Demander à quelqu’un de repartager le document.",
|
"filesharing.missingSeeds": "Si le téléchargement prend trop de temps c’est qu’il n’y a peut-être plus personne qui partage ce torrent. Demander à quelqu’un de repartager le document.",
|
||||||
"devices.devicesChanged" : " Vos périphériques ont changé, reconfigurer vos périphériques avec le menu paramètre",
|
"devices.devicesChanged": "Vos périphériques ont changé, reconfigurer vos périphériques avec le menu paramètre",
|
||||||
|
|
||||||
"device.audioUnsupported" : " Microphone non supporté",
|
"device.audioUnsupported": "Microphone non supporté",
|
||||||
"device.activateAudio" : " Activer l'audio",
|
"device.activateAudio": "Activer l'audio",
|
||||||
"device.muteAudio" : " Désactiver l'audio",
|
"device.muteAudio": "Désactiver l'audio",
|
||||||
"device.unMuteAudio" : " Réactiver l'audio",
|
"device.unMuteAudio": "Réactiver l'audio",
|
||||||
|
|
||||||
"device.videoUnsupported" : " Vidéo non supporté",
|
"device.videoUnsupported": "Vidéo non supporté",
|
||||||
"device.startVideo" : " Démarrer la vidéo",
|
"device.startVideo": "Démarrer la vidéo",
|
||||||
"device.stopVideo" : " Arrêter la vidéo",
|
"device.stopVideo": "Arrêter la vidéo",
|
||||||
|
|
||||||
"device.screenSharingUnsupported" : " Partage d'écran non supporté",
|
"device.screenSharingUnsupported": "Partage d'écran non supporté",
|
||||||
"device.startScreenSharing" : " Démarrer le partage d 'écran'",
|
"device.startScreenSharing": "Démarrer le partage d 'écran'",
|
||||||
"device.stopScreenSharing" : " Arrêter le partage d'écran",
|
"device.stopScreenSharing": "Arrêter le partage d'écran",
|
||||||
|
|
||||||
"devices.microphoneDisconnected" : " Microphone déconnecté",
|
"devices.microphoneDisconnected": "Microphone déconnecté",
|
||||||
"devices.microphoneError" : " Une erreur est apparue lors de l'accès à votre microphone",
|
"devices.microphoneError": "Une erreur est apparue lors de l'accès à votre microphone",
|
||||||
"devices.microPhoneMute" : " Désactiver le microphone",
|
"devices.microPhoneMute": "Désactiver le microphone",
|
||||||
"devices.micophoneUnMute" : " Réactiver le microphone",
|
"devices.micophoneUnMute": "Réactiver le microphone",
|
||||||
"devices.microphoneEnable" : " Activer le microphone",
|
"devices.microphoneEnable": "Activer le microphone",
|
||||||
"devices.microphoneMuteError" : " Impossible de désactiver le microphone",
|
"devices.microphoneMuteError": "Impossible de désactiver le microphone",
|
||||||
"devices.microphoneUnMuteError" : " Impossible de réactiver le microphone",
|
"devices.microphoneUnMuteError": "Impossible de réactiver le microphone",
|
||||||
|
|
||||||
"devices.screenSharingDisconnected" : " Partage d'écran déconnecté",
|
"devices.screenSharingDisconnected": "Partage d'écran déconnecté",
|
||||||
"devices.screenSharingError" : " Une erreur est apparue lors de l'accès à votre partage d'écran",
|
"devices.screenSharingError": "Une erreur est apparue lors de l'accès à votre partage d'écran",
|
||||||
|
|
||||||
"devices.cameraDisconnected" : " Caméra déconnectée",
|
"devices.cameraDisconnected": "Caméra déconnectée",
|
||||||
"devices.cameraError" : " Une erreur est apparue lors de l'accès à votre caméra"
|
"devices.cameraError": "Une erreur est apparue lors de l'accès à votre caméra"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"room.spotlights": "Učesnici u fokusu",
|
"room.spotlights": "Učesnici u fokusu",
|
||||||
"room.passive": "Pasivni učesnici",
|
"room.passive": "Pasivni učesnici",
|
||||||
"room.videoPaused": "Video pauziran",
|
"room.videoPaused": "Video pauziran",
|
||||||
|
"room.muteAll": null,
|
||||||
|
"room.stopAllVideo": null,
|
||||||
|
"room.closeMeeting": null,
|
||||||
|
|
||||||
"tooltip.login": "Prijava",
|
"tooltip.login": "Prijava",
|
||||||
"tooltip.logout": "Odjava",
|
"tooltip.logout": "Odjava",
|
||||||
|
|
@ -60,6 +63,7 @@
|
||||||
"tooltip.lobby": "Prikaži predvorje",
|
"tooltip.lobby": "Prikaži predvorje",
|
||||||
"tooltip.settings": "Prikaži postavke",
|
"tooltip.settings": "Prikaži postavke",
|
||||||
"tooltip.participants": "Pokažite sudionike",
|
"tooltip.participants": "Pokažite sudionike",
|
||||||
|
"tooltip.kickParticipant": null,
|
||||||
|
|
||||||
"label.roomName": "Naziv sobe",
|
"label.roomName": "Naziv sobe",
|
||||||
"label.chooseRoomButton": "Nastavi",
|
"label.chooseRoomButton": "Nastavi",
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"socket.reconnected": "Sikeres újarkapcsolódás",
|
"socket.reconnected": "Sikeres újarkapcsolódás",
|
||||||
"socket.requestError": "Sikertelen szerver lekérés",
|
"socket.requestError": "Sikertelen szerver lekérés",
|
||||||
|
|
||||||
"room.chooseRoom": "Choose the name of the room you would like to join",
|
"room.chooseRoom": null,
|
||||||
"room.cookieConsent": "Ez a weblap a felhasználói élmény fokozása miatt sütiket használ",
|
"room.cookieConsent": "Ez a weblap a felhasználói élmény fokozása miatt sütiket használ",
|
||||||
"room.consentUnderstand": "I understand",
|
"room.consentUnderstand": "I understand",
|
||||||
"room.joined": "Csatlakozátál a konferenciához",
|
"room.joined": "Csatlakozátál a konferenciához",
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"room.spotlights": "Látható résztvevők",
|
"room.spotlights": "Látható résztvevők",
|
||||||
"room.passive": "Passzív résztvevők",
|
"room.passive": "Passzív résztvevők",
|
||||||
"room.videoPaused": "Ez a videóstream szünetel",
|
"room.videoPaused": "Ez a videóstream szünetel",
|
||||||
|
"room.muteAll": null,
|
||||||
|
"room.stopAllVideo": null,
|
||||||
|
"room.closeMeeting": null,
|
||||||
|
|
||||||
"tooltip.login": "Belépés",
|
"tooltip.login": "Belépés",
|
||||||
"tooltip.logout": "Kilépés",
|
"tooltip.logout": "Kilépés",
|
||||||
|
|
@ -60,6 +63,7 @@
|
||||||
"tooltip.lobby": "Az előszobában várakozók listája",
|
"tooltip.lobby": "Az előszobában várakozók listája",
|
||||||
"tooltip.settings": "Beállítások",
|
"tooltip.settings": "Beállítások",
|
||||||
"tooltip.participants": "Résztvevők",
|
"tooltip.participants": "Résztvevők",
|
||||||
|
"tooltip.kickParticipant": null,
|
||||||
|
|
||||||
"label.roomName": "Konferencia",
|
"label.roomName": "Konferencia",
|
||||||
"label.chooseRoomButton": "Tovább",
|
"label.chooseRoomButton": "Tovább",
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"room.spotlights": "Deltakere i fokus",
|
"room.spotlights": "Deltakere i fokus",
|
||||||
"room.passive": "Passive deltakere",
|
"room.passive": "Passive deltakere",
|
||||||
"room.videoPaused": "Denne videoen er inaktiv",
|
"room.videoPaused": "Denne videoen er inaktiv",
|
||||||
|
"room.muteAll": "Demp alle",
|
||||||
|
"room.stopAllVideo": "Stopp all video",
|
||||||
|
"room.closeMeeting": "Avslutt møte",
|
||||||
|
|
||||||
"tooltip.login": "Logg in",
|
"tooltip.login": "Logg in",
|
||||||
"tooltip.logout": "Logg ut",
|
"tooltip.logout": "Logg ut",
|
||||||
|
|
@ -60,6 +63,7 @@
|
||||||
"tooltip.lobby": "Vis lobby",
|
"tooltip.lobby": "Vis lobby",
|
||||||
"tooltip.settings": "Vis innstillinger",
|
"tooltip.settings": "Vis innstillinger",
|
||||||
"tooltip.participants": "Vis deltakere",
|
"tooltip.participants": "Vis deltakere",
|
||||||
|
"tooltip.kickParticipant": "Spark ut deltaker",
|
||||||
|
|
||||||
"label.roomName": "Møtenavn",
|
"label.roomName": "Møtenavn",
|
||||||
"label.chooseRoomButton": "Fortsett",
|
"label.chooseRoomButton": "Fortsett",
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"room.spotlights": "Aktywni uczestnicy",
|
"room.spotlights": "Aktywni uczestnicy",
|
||||||
"room.passive": "Pasywni uczestnicy",
|
"room.passive": "Pasywni uczestnicy",
|
||||||
"room.videoPaused": "To wideo jest wstrzymane.",
|
"room.videoPaused": "To wideo jest wstrzymane.",
|
||||||
|
"room.muteAll": null,
|
||||||
|
"room.stopAllVideo": null,
|
||||||
|
"room.closeMeeting": null,
|
||||||
|
|
||||||
"tooltip.login": "Zaloguj",
|
"tooltip.login": "Zaloguj",
|
||||||
"tooltip.logout": "Wyloguj",
|
"tooltip.logout": "Wyloguj",
|
||||||
|
|
@ -60,6 +63,7 @@
|
||||||
"tooltip.lobby": "Pokaż poczekalnię",
|
"tooltip.lobby": "Pokaż poczekalnię",
|
||||||
"tooltip.settings": "Pokaż ustawienia",
|
"tooltip.settings": "Pokaż ustawienia",
|
||||||
"tooltip.participants": "Pokaż uczestników",
|
"tooltip.participants": "Pokaż uczestników",
|
||||||
|
"tooltip.kickParticipant": null,
|
||||||
|
|
||||||
"label.roomName": "Nazwa konferencji",
|
"label.roomName": "Nazwa konferencji",
|
||||||
"label.chooseRoomButton": "Kontynuuj",
|
"label.chooseRoomButton": "Kontynuuj",
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"room.spotlights": "Participantes em foco",
|
"room.spotlights": "Participantes em foco",
|
||||||
"room.passive": "Participantes passivos",
|
"room.passive": "Participantes passivos",
|
||||||
"room.videoPaused": "Este vídeo está em pausa",
|
"room.videoPaused": "Este vídeo está em pausa",
|
||||||
|
"room.muteAll": null,
|
||||||
|
"room.stopAllVideo": null,
|
||||||
|
"room.closeMeeting": null,
|
||||||
|
|
||||||
"tooltip.login": "Entrar",
|
"tooltip.login": "Entrar",
|
||||||
"tooltip.logout": "Sair",
|
"tooltip.logout": "Sair",
|
||||||
|
|
@ -60,6 +63,7 @@
|
||||||
"tooltip.lobby": "Apresentar sala de espera",
|
"tooltip.lobby": "Apresentar sala de espera",
|
||||||
"tooltip.settings": "Apresentar definições",
|
"tooltip.settings": "Apresentar definições",
|
||||||
"tooltip.participants": "Apresentar participantes",
|
"tooltip.participants": "Apresentar participantes",
|
||||||
|
"tooltip.kickParticipant": null,
|
||||||
|
|
||||||
"label.roomName": "Nome da sala",
|
"label.roomName": "Nome da sala",
|
||||||
"label.chooseRoomButton": "Continuar",
|
"label.chooseRoomButton": "Continuar",
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"room.spotlights": "Participanți în Spotlight",
|
"room.spotlights": "Participanți în Spotlight",
|
||||||
"room.passive": "Participanți pasivi",
|
"room.passive": "Participanți pasivi",
|
||||||
"room.videoPaused": "Acest video este pus pe pauză",
|
"room.videoPaused": "Acest video este pus pe pauză",
|
||||||
|
"room.muteAll": null,
|
||||||
|
"room.stopAllVideo": null,
|
||||||
|
"room.closeMeeting": null,
|
||||||
|
|
||||||
"tooltip.login": "Intră în cont",
|
"tooltip.login": "Intră în cont",
|
||||||
"tooltip.logout": "Deconectare",
|
"tooltip.logout": "Deconectare",
|
||||||
|
|
@ -58,7 +61,9 @@
|
||||||
"tooltip.enterFullscreen": "Modul ecran complet",
|
"tooltip.enterFullscreen": "Modul ecran complet",
|
||||||
"tooltip.leaveFullscreen": "Ieșire din modul ecran complet",
|
"tooltip.leaveFullscreen": "Ieșire din modul ecran complet",
|
||||||
"tooltip.lobby": "Arată holul",
|
"tooltip.lobby": "Arată holul",
|
||||||
"tooltip.settings": "Arată participanții",
|
"tooltip.settings": "Arată setăile",
|
||||||
|
"tooltip.participants": null,
|
||||||
|
"tooltip.kickParticipant": null,
|
||||||
|
|
||||||
"label.roomName": "Numele camerei",
|
"label.roomName": "Numele camerei",
|
||||||
"label.chooseRoomButton": "Continuare",
|
"label.chooseRoomButton": "Continuare",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
|
const userRoles = require('../userRoles');
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
{
|
{
|
||||||
|
|
@ -63,18 +64,109 @@ module.exports =
|
||||||
// listeningRedirectPort disabled
|
// listeningRedirectPort disabled
|
||||||
// use case: loadbalancer backend
|
// use case: loadbalancer backend
|
||||||
httpOnly : false,
|
httpOnly : false,
|
||||||
// If this is set to true, only signed-in users will be able
|
// This function will be called on successful login through oidc.
|
||||||
// to join a room directly. Non-signed-in users (guests) will
|
// Use this function to map your oidc userinfo to the Peer object.
|
||||||
// always be put in the lobby regardless of room lock status.
|
// The roomId is equal to the room name.
|
||||||
// If false, there is no difference between guests and signed-in
|
// See examples below.
|
||||||
// users when joining.
|
// Examples:
|
||||||
requireSignInToAccess : true,
|
/*
|
||||||
// This flag has no effect when requireSignInToAccess is false
|
// All authenicated users will be MODERATOR and AUTHENTICATED
|
||||||
// When truthy, the room will be open to all users when the first
|
userMapping : async ({ peer, roomId, userinfo }) =>
|
||||||
// authenticated user has already joined the room.
|
{
|
||||||
activateOnHostJoin : true,
|
peer.addRole(userRoles.MODERATOR);
|
||||||
|
peer.addRole(userRoles.AUTHENTICATED);
|
||||||
|
},
|
||||||
|
// All authenicated users will be AUTHENTICATED,
|
||||||
|
// and those with the moderator role set in the userinfo
|
||||||
|
// will also be MODERATOR
|
||||||
|
userMapping : async ({ peer, roomId, userinfo }) =>
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
Array.isArray(userinfo.meet_roles) &&
|
||||||
|
userinfo.meet_roles.includes('moderator')
|
||||||
|
)
|
||||||
|
{
|
||||||
|
peer.addRole(userRoles.MODERATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Array.isArray(userinfo.meet_roles) &&
|
||||||
|
userinfo.meet_roles.includes('meetingadmin')
|
||||||
|
)
|
||||||
|
{
|
||||||
|
peer.addRole(userRoles.ADMIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
peer.addRole(userRoles.AUTHENTICATED);
|
||||||
|
},
|
||||||
|
// All authenicated users will be AUTHENTICATED,
|
||||||
|
// and those with email ending with @example.com
|
||||||
|
// will also be MODERATOR
|
||||||
|
userMapping : async ({ peer, roomId, userinfo }) =>
|
||||||
|
{
|
||||||
|
if (userinfo.email && userinfo.email.endsWith('@example.com'))
|
||||||
|
{
|
||||||
|
peer.addRole(userRoles.MODERATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
peer.addRole(userRoles.AUTHENTICATED);
|
||||||
|
}
|
||||||
|
// All authenicated users will be AUTHENTICATED,
|
||||||
|
// and those with email ending with @example.com
|
||||||
|
// will also be MODERATOR
|
||||||
|
userMapping : async ({ peer, roomId, userinfo }) =>
|
||||||
|
{
|
||||||
|
if (userinfo.email && userinfo.email.endsWith('@example.com'))
|
||||||
|
{
|
||||||
|
peer.addRole(userRoles.MODERATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
peer.addRole(userRoles.AUTHENTICATED);
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
userMapping : async ({ peer, roomId, userinfo }) =>
|
||||||
|
{
|
||||||
|
if (userinfo.picture != null)
|
||||||
|
{
|
||||||
|
if (!userinfo.picture.match(/^http/g))
|
||||||
|
{
|
||||||
|
peer.picture = `data:image/jpeg;base64, ${userinfo.picture}`;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
peer.picture = userinfo.picture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userinfo.nickname != null)
|
||||||
|
{
|
||||||
|
peer.displayName = userinfo.nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userinfo.name != null)
|
||||||
|
{
|
||||||
|
peer.displayName = userinfo.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userinfo.email != null)
|
||||||
|
{
|
||||||
|
peer.email = userinfo.email;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Required roles for Access. All users have the role "ALL" by default.
|
||||||
|
// Other roles need to be added in the "userMapping" function. This
|
||||||
|
// is an Array of roles. userRoles.ADMIN have all priveleges and access
|
||||||
|
// always.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// [ userRoles.MODERATOR, userRoles.AUTHENTICATED ]
|
||||||
|
// This will allow all MODERATOR and AUTHENTICATED users access.
|
||||||
|
requiredRolesForAccess : [ userRoles.ALL ],
|
||||||
|
// When truthy, the room will be open to all users when as long as there
|
||||||
|
// are allready users in the room
|
||||||
|
activateOnHostJoin : true,
|
||||||
// Mediasoup settings
|
// Mediasoup settings
|
||||||
mediasoup :
|
mediasoup :
|
||||||
{
|
{
|
||||||
numWorkers : Object.keys(os.cpus()).length,
|
numWorkers : Object.keys(os.cpus()).length,
|
||||||
// mediasoup Worker settings.
|
// mediasoup Worker settings.
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ class Lobby extends EventEmitter
|
||||||
// Closed flag.
|
// Closed flag.
|
||||||
this._closed = false;
|
this._closed = false;
|
||||||
|
|
||||||
this._peers = new Map();
|
this._peers = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
close()
|
close()
|
||||||
|
|
@ -23,27 +23,28 @@ class Lobby extends EventEmitter
|
||||||
|
|
||||||
this._closed = true;
|
this._closed = true;
|
||||||
|
|
||||||
this._peers.forEach((peer) =>
|
// Close the peers.
|
||||||
|
for (const peer in this._peers)
|
||||||
{
|
{
|
||||||
if (!peer.closed)
|
if (!peer.closed)
|
||||||
peer.close();
|
peer.close();
|
||||||
});
|
}
|
||||||
|
|
||||||
this._peers.clear();
|
this._peers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkEmpty()
|
checkEmpty()
|
||||||
{
|
{
|
||||||
logger.info('checkEmpty()');
|
logger.info('checkEmpty()');
|
||||||
|
|
||||||
return this._peers.size === 0;
|
return Object.keys(this._peers).length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
peerList()
|
peerList()
|
||||||
{
|
{
|
||||||
logger.info('peerList()');
|
logger.info('peerList()');
|
||||||
|
|
||||||
return Array.from(this._peers.values()).map((peer) =>
|
return Object.values(this._peers).map((peer) =>
|
||||||
({
|
({
|
||||||
peerId : peer.id,
|
peerId : peer.id,
|
||||||
displayName : peer.displayName
|
displayName : peer.displayName
|
||||||
|
|
@ -52,38 +53,42 @@ class Lobby extends EventEmitter
|
||||||
|
|
||||||
hasPeer(peerId)
|
hasPeer(peerId)
|
||||||
{
|
{
|
||||||
return this._peers.has(peerId);
|
return this._peers[peerId] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
promoteAllPeers()
|
promoteAllPeers()
|
||||||
{
|
{
|
||||||
logger.info('promoteAllPeers()');
|
logger.info('promoteAllPeers()');
|
||||||
|
|
||||||
this._peers.forEach((peer) =>
|
for (const peer in this._peers)
|
||||||
{
|
{
|
||||||
if (peer.socket)
|
if (peer.socket)
|
||||||
this.promotePeer(peer.id);
|
this.promotePeer(peer.id);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
promotePeer(peerId)
|
promotePeer(peerId)
|
||||||
{
|
{
|
||||||
logger.info('promotePeer() [peer:"%s"]', peerId);
|
logger.info('promotePeer() [peer:"%s"]', peerId);
|
||||||
|
|
||||||
const peer = this._peers.get(peerId);
|
const peer = this._peers[peerId];
|
||||||
|
|
||||||
if (peer)
|
if (peer)
|
||||||
{
|
{
|
||||||
peer.socket.removeListener('request', peer.socketRequestHandler);
|
peer.socket.removeListener('request', peer.socketRequestHandler);
|
||||||
peer.removeListener('authenticationChanged', peer.authenticationHandler);
|
peer.removeListener('gotRole', peer.gotRoleHandler);
|
||||||
|
peer.removeListener('displayNameChanged', peer.displayNameChangeHandler);
|
||||||
|
peer.removeListener('pictureChanged', peer.pictureChangeHandler);
|
||||||
peer.removeListener('close', peer.closeHandler);
|
peer.removeListener('close', peer.closeHandler);
|
||||||
|
|
||||||
peer.socketRequestHandler = null;
|
peer.socketRequestHandler = null;
|
||||||
peer.authenticationHandler = null;
|
peer.gotRoleHandler = null;
|
||||||
|
peer.displayNameChangeHandler = null;
|
||||||
|
peer.pictureChangeHandler = null;
|
||||||
peer.closeHandler = null;
|
peer.closeHandler = null;
|
||||||
|
|
||||||
this.emit('promotePeer', peer);
|
this.emit('promotePeer', peer);
|
||||||
this._peers.delete(peerId);
|
delete this._peers[peerId];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,16 +117,25 @@ class Lobby extends EventEmitter
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
peer.authenticationHandler = () =>
|
peer.gotRoleHandler = () =>
|
||||||
{
|
{
|
||||||
logger.info('parkPeer() | authenticationChange [peer:"%s"]', peer.id);
|
logger.info('parkPeer() | rolesChange [peer:"%s"]', peer.id);
|
||||||
|
|
||||||
if (peer.authenticated)
|
this.emit('peerRolesChanged', peer);
|
||||||
{
|
};
|
||||||
this.emit('changeDisplayName', peer);
|
|
||||||
this.emit('changePicture', peer);
|
peer.displayNameChangeHandler = () =>
|
||||||
this.emit('peerAuthenticated', peer);
|
{
|
||||||
}
|
logger.info('parkPeer() | displayNameChange [peer:"%s"]', peer.id);
|
||||||
|
|
||||||
|
this.emit('changeDisplayName', peer);
|
||||||
|
};
|
||||||
|
|
||||||
|
peer.pictureChangeHandler = () =>
|
||||||
|
{
|
||||||
|
logger.info('parkPeer() | pictureChange [peer:"%s"]', peer.id);
|
||||||
|
|
||||||
|
this.emit('changePicture', peer);
|
||||||
};
|
};
|
||||||
|
|
||||||
peer.closeHandler = () =>
|
peer.closeHandler = () =>
|
||||||
|
|
@ -133,7 +147,7 @@ class Lobby extends EventEmitter
|
||||||
|
|
||||||
this.emit('peerClosed', peer);
|
this.emit('peerClosed', peer);
|
||||||
|
|
||||||
this._peers.delete(peer.id);
|
delete this._peers[peer.id];
|
||||||
|
|
||||||
if (this.checkEmpty())
|
if (this.checkEmpty())
|
||||||
this.emit('lobbyEmpty');
|
this.emit('lobbyEmpty');
|
||||||
|
|
@ -141,9 +155,11 @@ class Lobby extends EventEmitter
|
||||||
|
|
||||||
this._notification(peer.socket, 'enteredLobby');
|
this._notification(peer.socket, 'enteredLobby');
|
||||||
|
|
||||||
this._peers.set(peer.id, peer);
|
this._peers[peer.id] = peer;
|
||||||
|
|
||||||
peer.on('authenticationChanged', peer.authenticationHandler);
|
peer.on('gotRole', peer.gotRoleHandler);
|
||||||
|
peer.on('displayNameChanged', peer.displayNameChangeHandler);
|
||||||
|
peer.on('pictureChanged', peer.pictureChangeHandler);
|
||||||
|
|
||||||
peer.socket.on('request', peer.socketRequestHandler);
|
peer.socket.on('request', peer.socketRequestHandler);
|
||||||
|
|
||||||
|
|
@ -169,8 +185,6 @@ class Lobby extends EventEmitter
|
||||||
|
|
||||||
peer.displayName = displayName;
|
peer.displayName = displayName;
|
||||||
|
|
||||||
this.emit('changeDisplayName', peer);
|
|
||||||
|
|
||||||
cb();
|
cb();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -181,8 +195,6 @@ class Lobby extends EventEmitter
|
||||||
|
|
||||||
peer.picture = picture;
|
peer.picture = picture;
|
||||||
|
|
||||||
this.emit('changePicture', peer);
|
|
||||||
|
|
||||||
cb();
|
cb();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
const EventEmitter = require('events').EventEmitter;
|
const EventEmitter = require('events').EventEmitter;
|
||||||
|
const userRoles = require('../userRoles');
|
||||||
const Logger = require('./Logger');
|
const Logger = require('./Logger');
|
||||||
|
|
||||||
const logger = new Logger('Peer');
|
const logger = new Logger('Peer');
|
||||||
|
|
||||||
class Peer extends EventEmitter
|
class Peer extends EventEmitter
|
||||||
{
|
{
|
||||||
constructor({ id, socket })
|
constructor({ id, roomId, socket })
|
||||||
{
|
{
|
||||||
logger.info('constructor() [id:"%s", socket:"%s"]', id, socket.id);
|
logger.info('constructor() [id:"%s"]', id);
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this._id = id;
|
this._id = id;
|
||||||
|
|
||||||
|
this._roomId = roomId;
|
||||||
|
|
||||||
this._authId = null;
|
this._authId = null;
|
||||||
|
|
||||||
this._socket = socket;
|
this._socket = socket;
|
||||||
|
|
@ -22,7 +25,7 @@ class Peer extends EventEmitter
|
||||||
|
|
||||||
this._inLobby = false;
|
this._inLobby = false;
|
||||||
|
|
||||||
this._authenticated = false;
|
this._roles = [ userRoles.ALL ];
|
||||||
|
|
||||||
this._displayName = false;
|
this._displayName = false;
|
||||||
|
|
||||||
|
|
@ -40,8 +43,6 @@ class Peer extends EventEmitter
|
||||||
|
|
||||||
this._consumers = new Map();
|
this._consumers = new Map();
|
||||||
|
|
||||||
this._checkAuthentication();
|
|
||||||
|
|
||||||
this._handlePeer();
|
this._handlePeer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,56 +59,25 @@ class Peer extends EventEmitter
|
||||||
transport.close();
|
transport.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this._socket)
|
if (this.socket)
|
||||||
this._socket.disconnect(true);
|
this.socket.disconnect(true);
|
||||||
|
|
||||||
this.emit('close');
|
this.emit('close');
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlePeer()
|
_handlePeer()
|
||||||
{
|
{
|
||||||
this.socket.use((packet, next) =>
|
if (this.socket)
|
||||||
{
|
{
|
||||||
this._checkAuthentication();
|
this.socket.on('disconnect', () =>
|
||||||
|
{
|
||||||
|
if (this.closed)
|
||||||
|
return;
|
||||||
|
|
||||||
return next();
|
logger.debug('"disconnect" event [id:%s]', this.id);
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on('disconnect', () =>
|
this.close();
|
||||||
{
|
});
|
||||||
if (this.closed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
logger.debug('"disconnect" event [id:%s]', this.id);
|
|
||||||
|
|
||||||
this.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_checkAuthentication()
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
Boolean(this.socket.handshake.session.passport) &&
|
|
||||||
Boolean(this.socket.handshake.session.passport.user)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
displayName,
|
|
||||||
picture,
|
|
||||||
email
|
|
||||||
} = this.socket.handshake.session.passport.user;
|
|
||||||
|
|
||||||
id && (this.authId = id);
|
|
||||||
displayName && (this.displayName = displayName);
|
|
||||||
picture && (this.picture = picture);
|
|
||||||
email && (this.email = email);
|
|
||||||
|
|
||||||
this.authenticated = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.authenticated = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,6 +91,16 @@ class Peer extends EventEmitter
|
||||||
this._id = id;
|
this._id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get roomId()
|
||||||
|
{
|
||||||
|
return this._roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
set roomId(roomId)
|
||||||
|
{
|
||||||
|
this._roomId = roomId;
|
||||||
|
}
|
||||||
|
|
||||||
get authId()
|
get authId()
|
||||||
{
|
{
|
||||||
return this._authId;
|
return this._authId;
|
||||||
|
|
@ -166,21 +146,9 @@ class Peer extends EventEmitter
|
||||||
this._inLobby = inLobby;
|
this._inLobby = inLobby;
|
||||||
}
|
}
|
||||||
|
|
||||||
get authenticated()
|
get roles()
|
||||||
{
|
{
|
||||||
return this._authenticated;
|
return this._roles;
|
||||||
}
|
|
||||||
|
|
||||||
set authenticated(authenticated)
|
|
||||||
{
|
|
||||||
if (authenticated !== this._authenticated)
|
|
||||||
{
|
|
||||||
const oldAuthenticated = this._authenticated;
|
|
||||||
|
|
||||||
this._authenticated = authenticated;
|
|
||||||
|
|
||||||
this.emit('authenticationChanged', { oldAuthenticated });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get displayName()
|
get displayName()
|
||||||
|
|
@ -262,6 +230,35 @@ class Peer extends EventEmitter
|
||||||
return this._consumers;
|
return this._consumers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addRole(newRole)
|
||||||
|
{
|
||||||
|
if (!this._roles.includes(newRole))
|
||||||
|
{
|
||||||
|
this._roles.push(newRole);
|
||||||
|
|
||||||
|
logger.info('addRole() | [newRole:"%s]"', newRole);
|
||||||
|
|
||||||
|
this.emit('gotRole', { newRole });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeRole(oldRole)
|
||||||
|
{
|
||||||
|
if (this._roles.includes(oldRole))
|
||||||
|
{
|
||||||
|
this._roles = this._roles.filter((role) => role !== oldRole);
|
||||||
|
|
||||||
|
logger.info('removeRole() | [oldRole:"%s]"', oldRole);
|
||||||
|
|
||||||
|
this.emit('lostRole', { oldRole });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasRole(role)
|
||||||
|
{
|
||||||
|
return this._roles.includes(role);
|
||||||
|
}
|
||||||
|
|
||||||
addTransport(id, transport)
|
addTransport(id, transport)
|
||||||
{
|
{
|
||||||
this.transports.set(id, transport);
|
this.transports.set(id, transport);
|
||||||
|
|
@ -319,7 +316,8 @@ class Peer extends EventEmitter
|
||||||
{
|
{
|
||||||
id : this.id,
|
id : this.id,
|
||||||
displayName : this.displayName,
|
displayName : this.displayName,
|
||||||
picture : this.picture
|
picture : this.picture,
|
||||||
|
roles : this.roles
|
||||||
};
|
};
|
||||||
|
|
||||||
return peerInfo;
|
return peerInfo;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ const EventEmitter = require('events').EventEmitter;
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const Logger = require('./Logger');
|
const Logger = require('./Logger');
|
||||||
const Lobby = require('./Lobby');
|
const Lobby = require('./Lobby');
|
||||||
|
const userRoles = require('../userRoles');
|
||||||
const config = require('../config/config');
|
const config = require('../config/config');
|
||||||
|
|
||||||
const logger = new Logger('Room');
|
const logger = new Logger('Room');
|
||||||
|
|
@ -54,6 +55,12 @@ class Room extends EventEmitter
|
||||||
// Locked flag.
|
// Locked flag.
|
||||||
this._locked = false;
|
this._locked = false;
|
||||||
|
|
||||||
|
// Required roles to access
|
||||||
|
this._requiredRoles = [ userRoles.ALL ];
|
||||||
|
|
||||||
|
if ('requiredRolesForAccess' in config)
|
||||||
|
this._requiredRoles = config.requiredRolesForAccess;
|
||||||
|
|
||||||
// if true: accessCode is a possibility to open the room
|
// if true: accessCode is a possibility to open the room
|
||||||
this._joinByAccesCode = true;
|
this._joinByAccesCode = true;
|
||||||
|
|
||||||
|
|
@ -100,11 +107,8 @@ class Room extends EventEmitter
|
||||||
// Close the peers.
|
// Close the peers.
|
||||||
for (const peer in this._peers)
|
for (const peer in this._peers)
|
||||||
{
|
{
|
||||||
if (Object.prototype.hasOwnProperty.call(this._peers, peer))
|
if (!peer.closed)
|
||||||
{
|
peer.close();
|
||||||
if (!peer.closed)
|
|
||||||
peer.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._peers = null;
|
this._peers = null;
|
||||||
|
|
@ -118,26 +122,27 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
handlePeer(peer)
|
handlePeer(peer)
|
||||||
{
|
{
|
||||||
logger.info('handlePeer() [peer:"%s"]', peer.id);
|
logger.info('handlePeer() [peer:"%s", roles:"%s"]', peer.id, peer.roles);
|
||||||
|
|
||||||
// This will allow reconnects to join despite lock
|
// Allow reconnections, remove old peer
|
||||||
if (this._peers[peer.id])
|
if (this._peers[peer.id])
|
||||||
{
|
{
|
||||||
logger.warn(
|
logger.warn(
|
||||||
'handleConnection() | there is already a peer with same peerId [peer:"%s"]',
|
'handleConnection() | there is already a peer with same peerId [peer:"%s"]',
|
||||||
peer.id);
|
peer.id);
|
||||||
|
|
||||||
peer.close();
|
this._peers[peer.id].close();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
// Always let ADMIN in, even if locked
|
||||||
}
|
if (peer.roles.includes(userRoles.ADMIN))
|
||||||
|
this._peerJoining(peer);
|
||||||
else if (this._locked)
|
else if (this._locked)
|
||||||
{
|
|
||||||
this._parkPeer(peer);
|
this._parkPeer(peer);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
peer.authenticated ?
|
// If the user has a role in config.requiredRolesForAccess, let them in
|
||||||
|
peer.roles.some((role) => this._requiredRoles.includes(role)) ?
|
||||||
this._peerJoining(peer) :
|
this._peerJoining(peer) :
|
||||||
this._handleGuest(peer);
|
this._handleGuest(peer);
|
||||||
}
|
}
|
||||||
|
|
@ -145,21 +150,12 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
_handleGuest(peer)
|
_handleGuest(peer)
|
||||||
{
|
{
|
||||||
if (config.requireSignInToAccess)
|
if (config.activateOnHostJoin && !this.checkEmpty())
|
||||||
{
|
this._peerJoining(peer);
|
||||||
if (config.activateOnHostJoin && !this.checkEmpty())
|
|
||||||
{
|
|
||||||
this._peerJoining(peer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this._parkPeer(peer);
|
|
||||||
this._notification(peer.socket, 'signInRequired');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this._peerJoining(peer);
|
this._parkPeer(peer);
|
||||||
|
this._notification(peer.socket, 'signInRequired');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,9 +175,26 @@ class Room extends EventEmitter
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._lobby.on('peerAuthenticated', (peer) =>
|
this._lobby.on('peerRolesChanged', (peer) =>
|
||||||
{
|
{
|
||||||
!this._locked && this._lobby.promotePeer(peer.id);
|
// Always let admin in, even if locked
|
||||||
|
if (peer.roles.includes(userRoles.ADMIN))
|
||||||
|
{
|
||||||
|
this._lobby.promotePeer(peer.id);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user has a role in config.requiredRolesForAccess, let them in
|
||||||
|
if (
|
||||||
|
!this._locked &&
|
||||||
|
peer.roles.some((role) => this._requiredRoles.includes(role))
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this._lobby.promotePeer(peer.id);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._lobby.on('changeDisplayName', (changedPeer) =>
|
this._lobby.on('changeDisplayName', (changedPeer) =>
|
||||||
|
|
@ -304,7 +317,6 @@ class Room extends EventEmitter
|
||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// checks both room and lobby
|
|
||||||
checkEmpty()
|
checkEmpty()
|
||||||
{
|
{
|
||||||
return Object.keys(this._peers).length === 0;
|
return Object.keys(this._peers).length === 0;
|
||||||
|
|
@ -324,12 +336,8 @@ class Room extends EventEmitter
|
||||||
{
|
{
|
||||||
peer.socket.join(this._roomId);
|
peer.socket.join(this._roomId);
|
||||||
|
|
||||||
const index = this._lastN.indexOf(peer.id);
|
// If we don't have this peer, add to end
|
||||||
|
!this._lastN.includes(peer.id) && this._lastN.push(peer.id);
|
||||||
if (index === -1) // We don't have this peer, add to end
|
|
||||||
{
|
|
||||||
this._lastN.push(peer.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._peers[peer.id] = peer;
|
this._peers[peer.id] = peer;
|
||||||
|
|
||||||
|
|
@ -402,25 +410,17 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
// If the Peer was joined, notify all Peers.
|
// If the Peer was joined, notify all Peers.
|
||||||
if (peer.joined)
|
if (peer.joined)
|
||||||
{
|
|
||||||
this._notification(peer.socket, 'peerClosed', { peerId: peer.id }, true);
|
this._notification(peer.socket, 'peerClosed', { peerId: peer.id }, true);
|
||||||
}
|
|
||||||
|
|
||||||
const index = this._lastN.indexOf(peer.id);
|
// Remove from lastN
|
||||||
|
this._lastN = this._lastN.filter((id) => id !== peer.id);
|
||||||
if (index > -1) // We have this peer in the list, remove
|
|
||||||
{
|
|
||||||
this._lastN.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete this._peers[peer.id];
|
delete this._peers[peer.id];
|
||||||
|
|
||||||
// If this is the last Peer in the room and
|
// If this is the last Peer in the room and
|
||||||
// lobby is empty, close the room after a while.
|
// lobby is empty, close the room after a while.
|
||||||
if (this.checkEmpty() && this._lobby.checkEmpty())
|
if (this.checkEmpty() && this._lobby.checkEmpty())
|
||||||
{
|
|
||||||
this.selfDestructCountdown();
|
this.selfDestructCountdown();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
peer.on('displayNameChanged', ({ oldDisplayName }) =>
|
peer.on('displayNameChanged', ({ oldDisplayName }) =>
|
||||||
|
|
@ -449,6 +449,32 @@ class Room extends EventEmitter
|
||||||
picture : peer.picture
|
picture : peer.picture
|
||||||
}, true);
|
}, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
peer.on('gotRole', ({ newRole }) =>
|
||||||
|
{
|
||||||
|
// Ensure the Peer is joined.
|
||||||
|
if (!peer.joined)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Spread to others
|
||||||
|
this._notification(peer.socket, 'gotRole', {
|
||||||
|
peerId : peer.id,
|
||||||
|
role : newRole
|
||||||
|
}, true, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.on('lostRole', ({ oldRole }) =>
|
||||||
|
{
|
||||||
|
// Ensure the Peer is joined.
|
||||||
|
if (!peer.joined)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Spread to others
|
||||||
|
this._notification(peer.socket, 'lostRole', {
|
||||||
|
peerId : peer.id,
|
||||||
|
role : oldRole
|
||||||
|
}, true, true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _handleSocketRequest(peer, request, cb)
|
async _handleSocketRequest(peer, request, cb)
|
||||||
|
|
@ -464,27 +490,6 @@ class Room extends EventEmitter
|
||||||
|
|
||||||
case 'join':
|
case 'join':
|
||||||
{
|
{
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (peer.socket.handshake.session.passport.user.displayName)
|
|
||||||
{
|
|
||||||
this._notification(
|
|
||||||
peer.socket,
|
|
||||||
'changeDisplayname',
|
|
||||||
{
|
|
||||||
peerId : peer.id,
|
|
||||||
displayName : peer.socket.handshake.session.passport.user.displayName,
|
|
||||||
oldDisplayName : ''
|
|
||||||
},
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (error)
|
|
||||||
{
|
|
||||||
logger.error(error);
|
|
||||||
}
|
|
||||||
// Ensure the Peer is not already joined.
|
// Ensure the Peer is not already joined.
|
||||||
if (peer.joined)
|
if (peer.joined)
|
||||||
throw new Error('Peer already joined');
|
throw new Error('Peer already joined');
|
||||||
|
|
@ -512,7 +517,10 @@ class Room extends EventEmitter
|
||||||
.filter((joinedPeer) => joinedPeer.id !== peer.id)
|
.filter((joinedPeer) => joinedPeer.id !== peer.id)
|
||||||
.map((joinedPeer) => (joinedPeer.peerInfo));
|
.map((joinedPeer) => (joinedPeer.peerInfo));
|
||||||
|
|
||||||
cb(null, { peers: peerInfos });
|
cb(null, {
|
||||||
|
roles : peer.roles,
|
||||||
|
peers : peerInfos
|
||||||
|
});
|
||||||
|
|
||||||
// Mark the new Peer as joined.
|
// Mark the new Peer as joined.
|
||||||
peer.joined = true;
|
peer.joined = true;
|
||||||
|
|
@ -540,7 +548,8 @@ class Room extends EventEmitter
|
||||||
{
|
{
|
||||||
id : peer.id,
|
id : peer.id,
|
||||||
displayName : displayName,
|
displayName : displayName,
|
||||||
picture : picture
|
picture : picture,
|
||||||
|
roles : peer.roles
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1106,6 +1115,92 @@ class Room extends EventEmitter
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'moderator:muteAll':
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!peer.hasRole(userRoles.MODERATOR) &&
|
||||||
|
!peer.hasRole(userRoles.ADMIN)
|
||||||
|
)
|
||||||
|
throw new Error('peer does not have moderator priveleges');
|
||||||
|
|
||||||
|
// Spread to others
|
||||||
|
this._notification(peer.socket, 'moderator:mute', {
|
||||||
|
peerId : peer.id
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
cb();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'moderator:stopAllVideo':
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!peer.hasRole(userRoles.MODERATOR) &&
|
||||||
|
!peer.hasRole(userRoles.ADMIN)
|
||||||
|
)
|
||||||
|
throw new Error('peer does not have moderator priveleges');
|
||||||
|
|
||||||
|
// Spread to others
|
||||||
|
this._notification(peer.socket, 'moderator:stopVideo', {
|
||||||
|
peerId : peer.id
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
cb();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'moderator:closeMeeting':
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!peer.hasRole(userRoles.MODERATOR) &&
|
||||||
|
!peer.hasRole(userRoles.ADMIN)
|
||||||
|
)
|
||||||
|
throw new Error('peer does not have moderator priveleges');
|
||||||
|
|
||||||
|
this._notification(
|
||||||
|
peer.socket,
|
||||||
|
'moderator:kick',
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
cb();
|
||||||
|
|
||||||
|
// Close the room
|
||||||
|
this.close();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'moderator:kickPeer':
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!peer.hasRole(userRoles.MODERATOR) &&
|
||||||
|
!peer.hasRole(userRoles.ADMIN)
|
||||||
|
)
|
||||||
|
throw new Error('peer does not have moderator priveleges');
|
||||||
|
|
||||||
|
const { peerId } = request.data;
|
||||||
|
|
||||||
|
const kickPeer = this._peers[peerId];
|
||||||
|
|
||||||
|
if (!kickPeer)
|
||||||
|
throw new Error(`peer with id "${peerId}" not found`);
|
||||||
|
|
||||||
|
this._notification(
|
||||||
|
kickPeer.socket,
|
||||||
|
'moderator:kick'
|
||||||
|
);
|
||||||
|
|
||||||
|
kickPeer.close();
|
||||||
|
|
||||||
|
cb();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
logger.error('unknown request.method "%s"', request.method);
|
logger.error('unknown request.method "%s"', request.method);
|
||||||
|
|
@ -1319,13 +1414,16 @@ class Room extends EventEmitter
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_notification(socket, method, data = {}, broadcast = false)
|
_notification(socket, method, data = {}, broadcast = false, includeSender = false)
|
||||||
{
|
{
|
||||||
if (broadcast)
|
if (broadcast)
|
||||||
{
|
{
|
||||||
socket.broadcast.to(this._roomId).emit(
|
socket.broadcast.to(this._roomId).emit(
|
||||||
'notification', { method, data }
|
'notification', { method, data }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (includeSender)
|
||||||
|
socket.emit('notification', { method, data });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
132
server/server.js
132
server/server.js
|
|
@ -18,6 +18,7 @@ const Peer = require('./lib/Peer');
|
||||||
const base64 = require('base-64');
|
const base64 = require('base-64');
|
||||||
const helmet = require('helmet');
|
const helmet = require('helmet');
|
||||||
|
|
||||||
|
const userRoles = require('./userRoles');
|
||||||
const {
|
const {
|
||||||
loginHelper,
|
loginHelper,
|
||||||
logoutHelper
|
logoutHelper
|
||||||
|
|
@ -190,7 +191,7 @@ function setupLTI(ltiConfig)
|
||||||
}
|
}
|
||||||
if (lti.lis_person_name_full)
|
if (lti.lis_person_name_full)
|
||||||
{
|
{
|
||||||
user.displayName=lti.lis_person_name_full;
|
user.displayName = lti.lis_person_name_full;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform local authentication if necessary
|
// Perform local authentication if necessary
|
||||||
|
|
@ -241,51 +242,6 @@ function setupOIDC(oidcIssuer)
|
||||||
_claims : tokenset.claims
|
_claims : tokenset.claims
|
||||||
};
|
};
|
||||||
|
|
||||||
if (userinfo.picture != null)
|
|
||||||
{
|
|
||||||
if (!userinfo.picture.match(/^http/g))
|
|
||||||
{
|
|
||||||
user.picture = `data:image/jpeg;base64, ${userinfo.picture}`;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
user.picture = userinfo.picture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userinfo.nickname != null)
|
|
||||||
{
|
|
||||||
user.displayName = userinfo.nickname;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userinfo.name != null)
|
|
||||||
{
|
|
||||||
user.displayName = userinfo.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userinfo.email != null)
|
|
||||||
{
|
|
||||||
user.email = userinfo.email;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userinfo.given_name != null)
|
|
||||||
{
|
|
||||||
user.name={};
|
|
||||||
user.name.givenName = userinfo.given_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userinfo.family_name != null)
|
|
||||||
{
|
|
||||||
if (user.name == null) user.name={};
|
|
||||||
user.name.familyName = userinfo.family_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userinfo.middle_name != null)
|
|
||||||
{
|
|
||||||
if (user.name == null) user.name={};
|
|
||||||
user.name.middleName = userinfo.middle_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return done(null, user);
|
return done(null, user);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -324,7 +280,8 @@ async function setupAuth()
|
||||||
{
|
{
|
||||||
passport.authenticate('oidc', {
|
passport.authenticate('oidc', {
|
||||||
state : base64.encode(JSON.stringify({
|
state : base64.encode(JSON.stringify({
|
||||||
id : req.query.id
|
peerId : req.query.peerId,
|
||||||
|
roomId : req.query.roomId
|
||||||
}))
|
}))
|
||||||
})(req, res, next);
|
})(req, res, next);
|
||||||
});
|
});
|
||||||
|
|
@ -341,6 +298,19 @@ async function setupAuth()
|
||||||
// logout
|
// logout
|
||||||
app.get('/auth/logout', (req, res) =>
|
app.get('/auth/logout', (req, res) =>
|
||||||
{
|
{
|
||||||
|
const { peerId } = req.session;
|
||||||
|
|
||||||
|
const peer = peers.get(peerId);
|
||||||
|
|
||||||
|
if (peer)
|
||||||
|
{
|
||||||
|
for (const role of peer.roles)
|
||||||
|
{
|
||||||
|
if (role !== userRoles.ALL)
|
||||||
|
peer.removeRole(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
req.logout();
|
req.logout();
|
||||||
res.send(logoutHelper());
|
res.send(logoutHelper());
|
||||||
});
|
});
|
||||||
|
|
@ -349,35 +319,35 @@ async function setupAuth()
|
||||||
app.get(
|
app.get(
|
||||||
'/auth/callback',
|
'/auth/callback',
|
||||||
passport.authenticate('oidc', { failureRedirect: '/auth/login' }),
|
passport.authenticate('oidc', { failureRedirect: '/auth/login' }),
|
||||||
(req, res) =>
|
async (req, res) =>
|
||||||
{
|
{
|
||||||
const state = JSON.parse(base64.decode(req.query.state));
|
const state = JSON.parse(base64.decode(req.query.state));
|
||||||
|
|
||||||
let displayName;
|
const { peerId, roomId } = state;
|
||||||
let picture;
|
|
||||||
|
|
||||||
if (req.user != null)
|
req.session.peerId = peerId;
|
||||||
|
req.session.roomId = roomId;
|
||||||
|
|
||||||
|
let peer = peers.get(peerId);
|
||||||
|
|
||||||
|
if (!peer) // User has no socket session yet, make temporary
|
||||||
|
peer = new Peer({ id: peerId, roomId });
|
||||||
|
|
||||||
|
if (peer && peer.roomId !== roomId) // The peer is mischievous
|
||||||
|
throw new Error('peer authenticated with wrong room');
|
||||||
|
|
||||||
|
if (peer && typeof config.userMapping === 'function')
|
||||||
{
|
{
|
||||||
if (req.user.displayName != null)
|
await config.userMapping({
|
||||||
displayName = req.user.displayName;
|
peer,
|
||||||
else
|
roomId,
|
||||||
displayName = '';
|
userinfo : req.user._userinfo
|
||||||
|
});
|
||||||
if (req.user.picture != null)
|
|
||||||
picture = req.user.picture;
|
|
||||||
else
|
|
||||||
picture = '/static/media/buddy.403cb9f6.svg';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const peer = peers.get(state.id);
|
|
||||||
|
|
||||||
peer && (peer.displayName = displayName);
|
|
||||||
peer && (peer.picture = picture);
|
|
||||||
peer && (peer.authenticated = true);
|
|
||||||
|
|
||||||
res.send(loginHelper({
|
res.send(loginHelper({
|
||||||
displayName,
|
displayName : peer.displayName,
|
||||||
picture
|
picture : peer.picture
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -495,12 +465,36 @@ async function runWebSocketServer()
|
||||||
queue.push(async () =>
|
queue.push(async () =>
|
||||||
{
|
{
|
||||||
const room = await getOrCreateRoom({ roomId });
|
const room = await getOrCreateRoom({ roomId });
|
||||||
const peer = new Peer({ id: peerId, socket });
|
const peer = new Peer({ id: peerId, roomId, socket });
|
||||||
|
|
||||||
peers.set(peerId, peer);
|
peers.set(peerId, peer);
|
||||||
|
|
||||||
peer.on('close', () => peers.delete(peerId));
|
peer.on('close', () => peers.delete(peerId));
|
||||||
|
|
||||||
|
if (
|
||||||
|
Boolean(socket.handshake.session.passport) &&
|
||||||
|
Boolean(socket.handshake.session.passport.user)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
displayName,
|
||||||
|
picture,
|
||||||
|
email,
|
||||||
|
_userinfo
|
||||||
|
} = socket.handshake.session.passport.user;
|
||||||
|
|
||||||
|
peer.authId= id;
|
||||||
|
peer.displayName = displayName;
|
||||||
|
peer.picture = picture;
|
||||||
|
peer.email = email;
|
||||||
|
|
||||||
|
if (typeof config.userMapping === 'function')
|
||||||
|
{
|
||||||
|
await config.userMapping({ peer, roomId, userinfo: _userinfo });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
room.handlePeer(peer);
|
room.handlePeer(peer);
|
||||||
})
|
})
|
||||||
.catch((error) =>
|
.catch((error) =>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
module.exports = {
|
||||||
|
// Allowed to enter locked rooms + all other priveleges
|
||||||
|
ADMIN : 'admin',
|
||||||
|
// Allowed to enter restricted rooms if configured.
|
||||||
|
// Allowed to moderate users in a room (mute all,
|
||||||
|
// spotlight video, kick users)
|
||||||
|
MODERATOR : 'moderator',
|
||||||
|
// Same as MODERATOR, but can't moderate users
|
||||||
|
AUTHENTICATED : 'authenticated',
|
||||||
|
// No priveleges
|
||||||
|
ALL : 'normal'
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue