mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-05-14 12:27:51 +00:00
feat(prejoin_page): Add prejoin page
This commit is contained in:
committed by
Saúl Ibarra Corretgé
parent
5b53232964
commit
a45cbf41ef
197
react/features/prejoin/components/preview/CopyMeetingUrl.js
Normal file
197
react/features/prejoin/components/preview/CopyMeetingUrl.js
Normal file
@@ -0,0 +1,197 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { getCurrentConferenceUrl } from '../../../base/connection';
|
||||
import { Icon, IconCopy, IconCheck } from '../../../base/icons';
|
||||
import logger from '../../logger';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The meeting url.
|
||||
*/
|
||||
url: string,
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* If true it shows the 'copy link' message.
|
||||
*/
|
||||
showCopyLink: boolean,
|
||||
|
||||
/**
|
||||
* If true it shows the 'link copied' message.
|
||||
*/
|
||||
showLinkCopied: boolean,
|
||||
};
|
||||
|
||||
const COPY_TIMEOUT = 2000;
|
||||
|
||||
/**
|
||||
* Component used to copy meeting url on prejoin page.
|
||||
*/
|
||||
class CopyMeetingUrl extends Component<Props, State> {
|
||||
|
||||
textarea: Object;
|
||||
|
||||
/**
|
||||
* Initializes a new {@code Prejoin} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.textarea = React.createRef();
|
||||
this.state = {
|
||||
showCopyLink: false,
|
||||
showLinkCopied: false
|
||||
};
|
||||
this._copyUrl = this._copyUrl.bind(this);
|
||||
this._hideCopyLink = this._hideCopyLink.bind(this);
|
||||
this._hideLinkCopied = this._hideLinkCopied.bind(this);
|
||||
this._showCopyLink = this._showCopyLink.bind(this);
|
||||
this._showLinkCopied = this._showLinkCopied.bind(this);
|
||||
}
|
||||
|
||||
_copyUrl: () => void;
|
||||
|
||||
/**
|
||||
* Callback invoked to copy the url to clipboard.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_copyUrl() {
|
||||
const textarea = this.textarea.current;
|
||||
|
||||
try {
|
||||
textarea.select();
|
||||
document.execCommand('copy');
|
||||
textarea.blur();
|
||||
this._showLinkCopied();
|
||||
window.setTimeout(this._hideLinkCopied, COPY_TIMEOUT);
|
||||
} catch (err) {
|
||||
logger.error('error when copying the meeting url');
|
||||
}
|
||||
}
|
||||
|
||||
_hideLinkCopied: () => void;
|
||||
|
||||
/**
|
||||
* Hides the 'Link copied' message.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_hideLinkCopied() {
|
||||
this.setState({
|
||||
showLinkCopied: false
|
||||
});
|
||||
}
|
||||
|
||||
_hideCopyLink: () => void;
|
||||
|
||||
/**
|
||||
* Hides the 'Copy link' text.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_hideCopyLink() {
|
||||
this.setState({
|
||||
showCopyLink: false
|
||||
});
|
||||
}
|
||||
|
||||
_showCopyLink: () => void;
|
||||
|
||||
/**
|
||||
* Shows the dark 'Copy link' text on hover.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_showCopyLink() {
|
||||
this.setState({
|
||||
showCopyLink: true
|
||||
});
|
||||
}
|
||||
|
||||
_showLinkCopied: () => void;
|
||||
|
||||
/**
|
||||
* Shows the green 'Link copied' message.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_showLinkCopied() {
|
||||
this.setState({
|
||||
showLinkCopied: true,
|
||||
showCopyLink: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { showCopyLink, showLinkCopied } = this.state;
|
||||
const { url, t } = this.props;
|
||||
const { _copyUrl, _showCopyLink, _hideCopyLink } = this;
|
||||
const src = showLinkCopied ? IconCheck : IconCopy;
|
||||
const iconCls = showCopyLink || showCopyLink ? 'prejoin-copy-icon--white' : 'prejoin-copy-icon--light';
|
||||
|
||||
return (
|
||||
<div
|
||||
className = 'prejoin-copy-meeting'
|
||||
onMouseEnter = { _showCopyLink }
|
||||
onMouseLeave = { _hideCopyLink }>
|
||||
<div className = 'prejoin-copy-url'>{url}</div>
|
||||
{showCopyLink && <div
|
||||
className = 'prejoin-copy-badge prejoin-copy-badge--hover'
|
||||
onClick = { _copyUrl }>
|
||||
{t('prejoin.copyAndShare')}
|
||||
</div>}
|
||||
{showLinkCopied && <div
|
||||
className = 'prejoin-copy-badge prejoin-copy-badge--done'>
|
||||
{t('prejoin.linkCopied')}
|
||||
</div>}
|
||||
<Icon
|
||||
className = { `prejoin-copy-icon ${iconCls}` }
|
||||
size = { 24 }
|
||||
src = { src } />
|
||||
<textarea
|
||||
className = 'prejoin-copy-textarea'
|
||||
readOnly = { true }
|
||||
ref = { this.textarea }
|
||||
tabIndex = '-1'
|
||||
value = { url } />
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React {@code Component} props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
url: getCurrentConferenceUrl(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(translate(CopyMeetingUrl));
|
||||
83
react/features/prejoin/components/preview/DeviceStatus.js
Normal file
83
react/features/prejoin/components/preview/DeviceStatus.js
Normal file
@@ -0,0 +1,83 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Icon, IconCheck, IconExclamation } from '../../../base/icons';
|
||||
import { connect } from '../../../base/redux';
|
||||
import {
|
||||
getDeviceStatusType,
|
||||
getDeviceStatusText,
|
||||
getRawError
|
||||
} from '../../functions';
|
||||
|
||||
export type Props = {
|
||||
|
||||
/**
|
||||
* The text to be displayed in relation to the status of the audio/video devices.
|
||||
*/
|
||||
deviceStatusText: string,
|
||||
|
||||
/**
|
||||
* The type of status for current devices, controlling the background color of the text.
|
||||
* Can be `ok` or `warning`.
|
||||
*/
|
||||
deviceStatusType: string,
|
||||
|
||||
/**
|
||||
* The error coming from device configuration.
|
||||
*/
|
||||
rawError: string,
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
|
||||
const iconMap = {
|
||||
warning: {
|
||||
src: IconExclamation,
|
||||
className: 'prejoin-preview-status--warning'
|
||||
},
|
||||
ok: {
|
||||
src: IconCheck,
|
||||
className: 'prejoin-preview-status--ok'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Strip showing the current status of the devices.
|
||||
* User is informed if there are missing or malfunctioning devices.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function DeviceStatus({ deviceStatusType, deviceStatusText, rawError, t }: Props) {
|
||||
const { src, className } = iconMap[deviceStatusType];
|
||||
|
||||
return (
|
||||
<div className = { `prejoin-preview-status ${className}` }>
|
||||
<Icon
|
||||
className = 'prejoin-preview-icon'
|
||||
size = { 16 }
|
||||
src = { src } />
|
||||
<span className = 'prejoin-preview-error-desc'>{t(deviceStatusText)}</span>
|
||||
<span>{rawError}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React {@code Component} props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {{ deviceStatusText: string, deviceStatusText: string }}
|
||||
*/
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
deviceStatusText: getDeviceStatusText(state),
|
||||
deviceStatusType: getDeviceStatusType(state),
|
||||
rawError: getRawError(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(mapStateToProps)(DeviceStatus));
|
||||
80
react/features/prejoin/components/preview/ParticipantName.js
Normal file
80
react/features/prejoin/components/preview/ParticipantName.js
Normal file
@@ -0,0 +1,80 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { translate } from '../../../base/i18n';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Flag signaling if the name is ediable or not.
|
||||
*/
|
||||
isEditable: boolean,
|
||||
|
||||
/**
|
||||
* Sets the name for the joining user.
|
||||
*/
|
||||
setName: Function,
|
||||
|
||||
/**
|
||||
* Used to obtain translations.
|
||||
*/
|
||||
t: Function,
|
||||
|
||||
/**
|
||||
* The text to be displayed.
|
||||
*/
|
||||
value: string,
|
||||
};
|
||||
|
||||
/**
|
||||
* Participant name - can be an editable input or just the text name.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
class ParticipantName extends Component<Props> {
|
||||
/**
|
||||
* Initializes a new {@code ParticipantName} instance.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._onNameChange = this._onNameChange.bind(this);
|
||||
}
|
||||
|
||||
_onNameChange: () => void;
|
||||
|
||||
/**
|
||||
* Handler used for changing the guest user name.
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
_onNameChange({ target: { value } }) {
|
||||
this.props.setName(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { value, isEditable, t } = this.props;
|
||||
|
||||
return isEditable ? (
|
||||
<input
|
||||
className = 'prejoin-preview-name prejoin-preview-name--editable'
|
||||
onChange = { this._onNameChange }
|
||||
placeholder = { t('dialog.enterDisplayName') }
|
||||
value = { value } />
|
||||
)
|
||||
: <div className = 'prejoin-preview-name'>{value}</div>
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default translate(ParticipantName);
|
||||
75
react/features/prejoin/components/preview/Preview.js
Normal file
75
react/features/prejoin/components/preview/Preview.js
Normal file
@@ -0,0 +1,75 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { Video } from '../../../base/media';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { getActiveVideoTrack, getPrejoinName, isPrejoinVideoMuted } from '../../functions';
|
||||
|
||||
export type Props = {
|
||||
|
||||
/**
|
||||
* The name of the user that is about to join.
|
||||
*/
|
||||
name: string,
|
||||
|
||||
/**
|
||||
* Flag signaling the visibility of camera preview.
|
||||
*/
|
||||
showCameraPreview: boolean,
|
||||
|
||||
/**
|
||||
* The JitsiLocalTrack to display.
|
||||
*/
|
||||
videoTrack: ?Object,
|
||||
};
|
||||
|
||||
/**
|
||||
* Component showing the video preview and device status.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function Preview(props: Props) {
|
||||
const {
|
||||
name,
|
||||
showCameraPreview,
|
||||
videoTrack
|
||||
} = props;
|
||||
|
||||
if (showCameraPreview && videoTrack) {
|
||||
return (
|
||||
<div className = 'prejoin-preview'>
|
||||
<div className = 'prejoin-preview-overlay' />
|
||||
<Video
|
||||
className = 'flipVideoX prejoin-preview-video'
|
||||
videoTrack = {{ jitsiTrack: videoTrack }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className = 'prejoin-preview prejoin-preview--no-video'>
|
||||
<Avatar
|
||||
className = 'prejoin-preview-avatar'
|
||||
displayName = { name }
|
||||
size = { 200 } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the redux state to the React {@code Component} props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
name: getPrejoinName(state),
|
||||
videoTrack: getActiveVideoTrack(state),
|
||||
showCameraPreview: !isPrejoinVideoMuted(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(Preview);
|
||||
Reference in New Issue
Block a user