|
@ -84,56 +84,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.icon-container {
|
||||
display: none;
|
||||
|
||||
&.active {
|
||||
display: flex;
|
||||
width: calc(100% - 26px);
|
||||
padding: 8px 8px 8px 16px;
|
||||
|
||||
background: #2A3A4B;
|
||||
border: 1px solid #5E6D7A;
|
||||
border-top: none;
|
||||
border-radius: 0 0 3px 3px;
|
||||
|
||||
.copy-invite-icon, .provider-icon {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
place-content: center;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
&:hover > div:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
& > :not(:last-child) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.copy-invite-icon > div > svg > path {
|
||||
fill: #A4B8D1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.dial-in-display {
|
||||
.info-label {
|
||||
color: #A4B8D1;
|
||||
}
|
||||
|
||||
.dial-in-copy {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&.invite-buttons {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
|
|
|
@ -11,22 +11,20 @@ import { copyText } from '../util/copyText.web';
|
|||
const useStyles = makeStyles()((theme: Theme) => {
|
||||
return {
|
||||
copyButton: {
|
||||
...withPixelLineHeight(theme.typography.bodyLongRegular),
|
||||
...withPixelLineHeight(theme.typography.bodyShortBold),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
padding: '8px 8px 8px 16px',
|
||||
marginTop: 5,
|
||||
width: 'calc(100% - 24px)',
|
||||
height: 24,
|
||||
|
||||
padding: `${theme.spacing(2)} ${theme.spacing(3)}`,
|
||||
width: '100%',
|
||||
boxSizing: 'border-box',
|
||||
background: theme.palette.action01,
|
||||
cursor: 'pointer',
|
||||
color: theme.palette.text01,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.action01Hover,
|
||||
fontWeight: 600
|
||||
backgroundColor: theme.palette.action01Hover
|
||||
},
|
||||
|
||||
'&.clicked': {
|
||||
|
@ -34,9 +32,10 @@ const useStyles = makeStyles()((theme: Theme) => {
|
|||
},
|
||||
|
||||
'& > div > svg > path': {
|
||||
fill: theme.palette.text01
|
||||
fill: theme.palette.icon01
|
||||
}
|
||||
},
|
||||
|
||||
content: {
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
|
@ -47,18 +46,22 @@ const useStyles = makeStyles()((theme: Theme) => {
|
|||
'&.selected': {
|
||||
fontWeight: 600
|
||||
}
|
||||
},
|
||||
|
||||
icon: {
|
||||
marginRight: theme.spacing(2)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
let mounted: boolean;
|
||||
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* Css class to apply on container.
|
||||
*/
|
||||
className: string;
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* The displayed text.
|
||||
|
@ -84,14 +87,14 @@ type Props = {
|
|||
* The text that needs to be copied (might differ from the displayedText).
|
||||
*/
|
||||
textToCopy: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Component meant to enable users to copy the conference URL.
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
function CopyButton({ className = '', displayedText, textToCopy, textOnHover, textOnCopySuccess, id }: Props) {
|
||||
function CopyButton({ className = '', displayedText, textToCopy, textOnHover, textOnCopySuccess, id }: IProps) {
|
||||
const { classes, cx } = useStyles();
|
||||
const [ isClicked, setIsClicked ] = useState(false);
|
||||
const [ isHovered, setIsHovered ] = useState(false);
|
||||
|
@ -169,20 +172,26 @@ function CopyButton({ className = '', displayedText, textToCopy, textOnHover, te
|
|||
if (isClicked) {
|
||||
return (
|
||||
<>
|
||||
<Icon
|
||||
className = { classes.icon }
|
||||
size = { 24 }
|
||||
src = { IconCheck } />
|
||||
<div className = { cx(classes.content, 'selected') }>
|
||||
<span role = { 'alert' }>{ textOnCopySuccess }</span>
|
||||
</div>
|
||||
<Icon src = { IconCheck } />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Icon
|
||||
className = { classes.icon }
|
||||
size = { 24 }
|
||||
src = { IconCopy } />
|
||||
<div className = { classes.content }>
|
||||
<span> { isHovered ? textOnHover : displayedText } </span>
|
||||
</div>
|
||||
<Icon src = { IconCopy } />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 4C4 2.89543 4.89543 2 6 2H14C15.1046 2 16 2.89543 16 4H6V18C4.89543 18 4 17.1046 4 16V4ZM10 8V20H18V8H10ZM10 6H18C19.1046 6 20 6.89543 20 8V20C20 21.1046 19.1046 22 18 22H10C8.89543 22 8 21.1046 8 20V8C8 6.89543 8.89543 6 10 6Z"/>
|
||||
<path d="M11.25 3H19.5C20.3284 3 21 3.67157 21 4.5V12.75C21 13.5784 20.3284 14.25 19.5 14.25H15.75V15.75H19.5C21.1569 15.75 22.5 14.4069 22.5 12.75V4.5C22.5 2.84315 21.1569 1.5 19.5 1.5H11.25C9.59315 1.5 8.25 2.84315 8.25 4.5V8.25H9.75V4.5C9.75 3.67157 10.4216 3 11.25 3Z" fill="white" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.75 9.75H4.5C3.67157 9.75 3 10.4216 3 11.25V19.5C3 20.3284 3.67157 21 4.5 21H12.75C13.5784 21 14.25 20.3284 14.25 19.5V11.25C14.25 10.4216 13.5784 9.75 12.75 9.75ZM4.5 8.25C2.84315 8.25 1.5 9.59315 1.5 11.25V19.5C1.5 21.1569 2.84315 22.5 4.5 22.5H12.75C14.4069 22.5 15.75 21.1569 15.75 19.5V11.25C15.75 9.59315 14.4069 8.25 12.75 8.25H4.5Z" fill="white" />
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 374 B After Width: | Height: | Size: 797 B |
|
@ -1,3 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 4H20C21.1046 4 22 4.89543 22 6V18C22 19.1046 21.1046 20 20 20H4C2.89543 20 2 19.1046 2 18V6C2 4.89543 2.89543 4 4 4ZM4 8V18H20V8L12 12L4 8ZM20 6H4L12 10L20 6Z" fill="#A4B8D1"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 8.25C1.5 6.17893 3.17893 4.5 5.25 4.5H18.75C20.8211 4.5 22.5 6.17893 22.5 8.25V15.75C22.5 17.8211 20.8211 19.5 18.75 19.5H5.25C3.17893 19.5 1.5 17.8211 1.5 15.75V8.25ZM5.25 6H18.75C19.2678 6 19.7447 6.1749 20.125 6.46886L12.9 11.8875C12.3667 12.2875 11.6334 12.2875 11.1 11.8875L3.87508 6.46882C4.25531 6.17489 4.73223 6 5.25 6ZM3.05983 7.73238C3.0207 7.89857 3 8.07187 3 8.25V15.75C3 16.9926 4.00736 18 5.25 18H18.75C19.9926 18 21 16.9926 21 15.75V8.25C21 8.07189 20.9793 7.89861 20.9402 7.73244L13.8 13.0875C12.7334 13.8875 11.2667 13.8875 10.2 13.0875L3.05983 7.73238Z" fill="white" />
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 750 B |
|
@ -1,3 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.2359 10.3544H19.56V10.32H12V13.68H16.7479C16.054 15.6356 14.1935 17.04 12 17.04C9.21583 17.04 6.95998 14.7841 6.95998 12C6.95998 9.21583 9.21583 6.95998 12 6.95998C13.2846 6.95998 14.4543 7.44396 15.3436 8.23638L17.7192 5.86076C16.2197 4.46294 14.2132 3.59998 12 3.59998C7.36029 3.59998 3.59998 7.36029 3.59998 12C3.59998 16.6397 7.36029 20.4 12 20.4C16.6397 20.4 20.4 16.6397 20.4 12C20.4 11.4372 20.3426 10.8876 20.2359 10.3544Z" fill="#A4B8D1"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.2359 10.3544H19.56V10.32H12V13.68H16.748C16.054 15.6356 14.1935 17.04 12 17.04C9.21587 17.04 6.96001 14.7841 6.96001 12C6.96001 9.21583 9.21587 6.95998 12 6.95998C13.2846 6.95998 14.4544 7.44396 15.3436 8.23638L17.7192 5.86076C16.2197 4.46294 14.2132 3.59998 12 3.59998C7.36032 3.59998 3.60001 7.36029 3.60001 12C3.60001 16.6397 7.36032 20.4 12 20.4C16.6397 20.4 20.4 16.6397 20.4 12C20.4 11.4372 20.3426 10.8876 20.2359 10.3544Z" fill="white" />
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 605 B After Width: | Height: | Size: 607 B |
|
@ -51,7 +51,7 @@ export { default as IconDownload } from './download.svg';
|
|||
export { default as IconDragHandle } from './drag-handle.svg';
|
||||
export { default as IconE2EE } from './e2ee.svg';
|
||||
export { default as IconEdit } from './edit.svg';
|
||||
export { default as IconEmail } from './envelope.svg';
|
||||
export { default as IconEnvelope } from './envelope.svg';
|
||||
export { default as IconEventNote } from './event_note.svg';
|
||||
export { default as IconExclamation } from './exclamation.svg';
|
||||
export { default as IconExclamationSolid } from './exclamation-solid.svg';
|
||||
|
@ -93,7 +93,7 @@ export { default as IconNoiseSuppressionOff } from './noise-suppression-off.svg'
|
|||
export { default as IconNoiseSuppressionOn } from './noise-suppression-on.svg';
|
||||
export { default as IconNotificationJoin } from './navigate_next.svg';
|
||||
export { default as IconOpenInNew } from './open_in_new.svg';
|
||||
export { default as IconOutlook } from './office365.svg';
|
||||
export { default as IconOffice365 } from './office365.svg';
|
||||
export { default as IconParticipants } from './participants.svg';
|
||||
export { default as IconPhone } from './phone.svg';
|
||||
export { default as IconPin } from './enlarge.svg';
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 6L14.0138 2L20.0213 3.5V20.5L14.0138 22L3 18L14.0138 19.5V5L7.00501 6.5V16.5L3 18V6Z" fill="#A4B8D1"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.00006 6L14.0138 2L20.0213 3.5V20.5L14.0138 22L3.00006 18L14.0138 19.5V5L7.00507 6.5V16.5L3.00006 18V6Z" fill="white" />
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 258 B After Width: | Height: | Size: 279 B |
|
@ -1,3 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 3H9.10397L12.076 10.6035L15.0865 3H20.0561L12.573 21H7.57197L9.62033 16.2303L4 3Z" fill="#A4B8D1"/>
|
||||
<path d="M4 3H9.10397L12.076 10.6035L15.0865 3H20.0561L12.573 21H7.57197L9.62033 16.2303L4 3Z" fill="white" />
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 215 B After Width: | Height: | Size: 218 B |
|
@ -11,8 +11,6 @@ import { JitsiRecordingConstants } from '../../../../base/lib-jitsi-meet';
|
|||
import { connect } from '../../../../base/redux/functions';
|
||||
import Dialog from '../../../../base/ui/components/web/Dialog';
|
||||
import { isDynamicBrandingDataLoaded } from '../../../../dynamic-branding/functions.any';
|
||||
// @ts-ignore
|
||||
import EmbedMeetingTrigger from '../../../../embed-meeting/components/EmbedMeetingTrigger';
|
||||
import { isVpaasMeeting } from '../../../../jaas/functions';
|
||||
// @ts-ignore
|
||||
import { getActiveSession } from '../../../../recording';
|
||||
|
@ -29,12 +27,10 @@ import {
|
|||
// @ts-ignore
|
||||
} from '../../../functions';
|
||||
|
||||
// @ts-ignore
|
||||
import CopyMeetingLinkSection from './CopyMeetingLinkSection';
|
||||
import DialInLimit from './DialInLimit';
|
||||
// @ts-ignore
|
||||
import DialInSection from './DialInSection';
|
||||
// @ts-ignore
|
||||
import InviteByEmailSection from './InviteByEmailSection';
|
||||
// @ts-ignore
|
||||
import InviteContactsSection from './InviteContactsSection';
|
||||
|
@ -58,11 +54,6 @@ interface IProps extends WithTranslation {
|
|||
*/
|
||||
_emailSharingVisible: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not embed meeting should be visible.
|
||||
*/
|
||||
_embedMeetingVisible: boolean;
|
||||
|
||||
/**
|
||||
* The meeting invitation text.
|
||||
*/
|
||||
|
@ -122,7 +113,6 @@ interface IProps extends WithTranslation {
|
|||
*/
|
||||
function AddPeopleDialog({
|
||||
_dialIn,
|
||||
_embedMeetingVisible,
|
||||
_dialInVisible,
|
||||
_urlSharingVisible,
|
||||
_emailSharingVisible,
|
||||
|
@ -135,7 +125,8 @@ function AddPeopleDialog({
|
|||
_liveStreamViewURL,
|
||||
_phoneNumber,
|
||||
t,
|
||||
updateNumbers }: IProps) {
|
||||
updateNumbers
|
||||
}: IProps) {
|
||||
|
||||
/**
|
||||
* Updates the dial-in numbers.
|
||||
|
@ -181,7 +172,6 @@ function AddPeopleDialog({
|
|||
inviteTextiOS = { _invitationTextiOS } />
|
||||
: null
|
||||
}
|
||||
{ _embedMeetingVisible && <EmbedMeetingTrigger /> }
|
||||
<div className = 'invite-more-dialog separator' />
|
||||
{
|
||||
_liveStreamViewURL
|
||||
|
@ -221,7 +211,6 @@ function mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
|
|||
|
||||
return {
|
||||
_dialIn: dialIn,
|
||||
_embedMeetingVisible: !isVpaasMeeting(state) && isSharingEnabled(sharingFeatures.embed),
|
||||
_dialInVisible: isSharingEnabled(sharingFeatures.dialIn),
|
||||
_urlSharingVisible: isDynamicBrandingDataLoaded(state) && isSharingEnabled(sharingFeatures.url),
|
||||
_emailSharingVisible: isSharingEnabled(sharingFeatures.email),
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import CopyButton from '../../../../base/buttons/CopyButton.web';
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import { getDecodedURI } from '../../../../base/util';
|
||||
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function,
|
||||
|
||||
/**
|
||||
* The URL of the conference.
|
||||
*/
|
||||
url: string
|
||||
};
|
||||
|
||||
/**
|
||||
* Component meant to enable users to copy the conference URL.
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
function CopyMeetingLinkSection({ t, url }: Props) {
|
||||
return (
|
||||
<>
|
||||
<label htmlFor = { 'copy-button-id' }>{t('addPeople.shareLink')}</label>
|
||||
<CopyButton
|
||||
aria-label = { t('addPeople.copyLink') }
|
||||
className = 'invite-more-dialog-conference-url'
|
||||
displayedText = { getDecodedURI(url) }
|
||||
id = 'copy-button-id'
|
||||
textOnCopySuccess = { t('addPeople.linkCopied') }
|
||||
textOnHover = { t('addPeople.copyLink') }
|
||||
textToCopy = { url } />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(CopyMeetingLinkSection);
|
|
@ -0,0 +1,53 @@
|
|||
import { Theme } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import CopyButton from '../../../../base/buttons/CopyButton.web';
|
||||
import { getDecodedURI } from '../../../../base/util/uri';
|
||||
|
||||
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The URL of the conference.
|
||||
*/
|
||||
url: string;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles()((theme: Theme) => {
|
||||
return {
|
||||
label: {
|
||||
display: 'block',
|
||||
marginBottom: theme.spacing(2)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Component meant to enable users to copy the conference URL.
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
function CopyMeetingLinkSection({ url }: IProps) {
|
||||
const { classes } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<label
|
||||
className = { classes.label }
|
||||
htmlFor = { 'copy-button-id' }>{t('addPeople.shareLink')}</label>
|
||||
<CopyButton
|
||||
aria-label = { t('addPeople.copyLink') }
|
||||
className = 'invite-more-dialog-conference-url'
|
||||
displayedText = { getDecodedURI(url) }
|
||||
id = 'copy-button-id'
|
||||
textOnCopySuccess = { t('addPeople.linkCopied') }
|
||||
textOnHover = { t('addPeople.copyLink') }
|
||||
textToCopy = { url } />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default CopyMeetingLinkSection;
|
|
@ -1,34 +1,31 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import { Icon, IconCopy } from '../../../../base/icons';
|
||||
import { translate } from '../../../../base/i18n/functions';
|
||||
import Icon from '../../../../base/icons/components/Icon';
|
||||
import { IconCopy } from '../../../../base/icons/svg';
|
||||
import { copyText } from '../../../../base/util/copyText.web';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { _formatConferenceIDPin } from '../../../_utils';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link DialInNumber}.
|
||||
*/
|
||||
type Props = {
|
||||
interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* The numeric identifier for the current conference, used after dialing a
|
||||
* the number to join the conference.
|
||||
*/
|
||||
conferenceID: number,
|
||||
conferenceID: string | number;
|
||||
|
||||
/**
|
||||
* The phone number to dial to begin the process of dialing into a
|
||||
* conference.
|
||||
*/
|
||||
phoneNumber: string,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
phoneNumber: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* React {@code Component} responsible for displaying a telephone number and
|
||||
|
@ -36,7 +33,7 @@ type Props = {
|
|||
*
|
||||
* @augments Component
|
||||
*/
|
||||
class DialInNumber extends Component<Props> {
|
||||
class DialInNumber extends Component<IProps> {
|
||||
|
||||
/**
|
||||
* Initializes a new DialInNumber instance.
|
||||
|
@ -44,7 +41,7 @@ class DialInNumber extends Component<Props> {
|
|||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// Bind event handler so it is only bound once for every instance.
|
||||
|
@ -52,8 +49,6 @@ class DialInNumber extends Component<Props> {
|
|||
this._onCopyTextKeyPress = this._onCopyTextKeyPress.bind(this);
|
||||
}
|
||||
|
||||
_onCopyText: () => void;
|
||||
|
||||
/**
|
||||
* Copies the dial-in information to the clipboard.
|
||||
*
|
||||
|
@ -69,8 +64,6 @@ class DialInNumber extends Component<Props> {
|
|||
copyText(textToCopy);
|
||||
}
|
||||
|
||||
_onCopyTextKeyPress: (Object) => void;
|
||||
|
||||
/**
|
||||
* KeyPress handler for accessibility.
|
||||
*
|
||||
|
@ -78,7 +71,7 @@ class DialInNumber extends Component<Props> {
|
|||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onCopyTextKeyPress(e) {
|
||||
_onCopyTextKeyPress(e: React.KeyboardEvent) {
|
||||
if (e.key === ' ' || e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
this._onCopyText();
|
|
@ -1,91 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import { connect } from '../../../../base/redux';
|
||||
import { getDialInfoPageURL, hasMultipleNumbers } from '../../../functions';
|
||||
|
||||
import DialInNumber from './DialInNumber';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The numeric identifier for the current conference, used after dialing a
|
||||
* the number to join the conference.
|
||||
*/
|
||||
_conferenceID: number,
|
||||
|
||||
/**
|
||||
* The url of the page containing the dial-in numbers list.
|
||||
*/
|
||||
_dialInfoPageUrl: string,
|
||||
|
||||
/**
|
||||
* If multiple dial-in numbers are available.
|
||||
*/
|
||||
_hasMultipleNumbers: boolean;
|
||||
|
||||
/**
|
||||
* The phone number to dial to begin the process of dialing into a
|
||||
* conference.
|
||||
*/
|
||||
phoneNumber: string,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a ReactElement for showing how to dial into the conference, if
|
||||
* dialing in is available.
|
||||
*
|
||||
* @private
|
||||
* @returns {null|ReactElement}
|
||||
*/
|
||||
function DialInSection({
|
||||
_conferenceID,
|
||||
_dialInfoPageUrl,
|
||||
_hasMultipleNumbers,
|
||||
phoneNumber,
|
||||
t
|
||||
}: Props) {
|
||||
return (
|
||||
<div className = 'invite-more-dialog dial-in-display'>
|
||||
<DialInNumber
|
||||
conferenceID = { _conferenceID }
|
||||
phoneNumber = { phoneNumber } />
|
||||
{_hasMultipleNumbers ? <a
|
||||
className = 'more-numbers'
|
||||
href = { _dialInfoPageUrl }
|
||||
rel = 'noopener noreferrer'
|
||||
target = '_blank'>
|
||||
{ t('info.moreNumbers') }
|
||||
</a> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Maps (parts of) the Redux state to the associated props for the
|
||||
* {@code DialInLink} component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
const dialIn = state['features/invite'];
|
||||
|
||||
return {
|
||||
_conferenceID: dialIn.conferenceID,
|
||||
_dialInfoPageUrl: getDialInfoPageURL(state),
|
||||
_hasMultipleNumbers: hasMultipleNumbers(dialIn.numbers)
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(DialInSection));
|
|
@ -0,0 +1,75 @@
|
|||
import { Theme } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
import { withPixelLineHeight } from '../../../../base/styles/functions.web';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { getDialInfoPageURL, hasMultipleNumbers } from '../../../functions';
|
||||
|
||||
import DialInNumber from './DialInNumber';
|
||||
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The phone number to dial to begin the process of dialing into a
|
||||
* conference.
|
||||
*/
|
||||
phoneNumber: string;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles()((theme: Theme) => {
|
||||
return {
|
||||
container: {
|
||||
'& .info-label': {
|
||||
...withPixelLineHeight(theme.typography.bodyLongBold)
|
||||
}
|
||||
},
|
||||
|
||||
link: {
|
||||
...withPixelLineHeight(theme.typography.bodyLongRegular),
|
||||
color: theme.palette.link01,
|
||||
|
||||
'&:hover': {
|
||||
color: theme.palette.link01Hover
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns a ReactElement for showing how to dial into the conference, if
|
||||
* dialing in is available.
|
||||
*
|
||||
* @private
|
||||
* @returns {null|ReactElement}
|
||||
*/
|
||||
function DialInSection({
|
||||
phoneNumber
|
||||
}: IProps) {
|
||||
const { classes, cx } = useStyles();
|
||||
const conferenceID = useSelector((state: IReduxState) => state['features/invite'].conferenceID);
|
||||
const dialInfoPageUrl: string = useSelector(getDialInfoPageURL);
|
||||
const showMoreNumbers = useSelector((state: IReduxState) => hasMultipleNumbers(state['features/invite'].numbers));
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className = { classes.container }>
|
||||
<DialInNumber
|
||||
conferenceID = { conferenceID ?? '' }
|
||||
phoneNumber = { phoneNumber } />
|
||||
{showMoreNumbers ? <a
|
||||
className = { cx('more-numbers', classes.link) }
|
||||
href = { dialInfoPageUrl }
|
||||
rel = 'noopener noreferrer'
|
||||
target = '_blank'>
|
||||
{ t('info.moreNumbers') }
|
||||
</a> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DialInSection;
|
|
@ -1,53 +1,76 @@
|
|||
// @flow
|
||||
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Theme } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { isIosMobileBrowser } from '../../../../base/environment/utils';
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import Icon from '../../../../base/icons/components/Icon';
|
||||
import {
|
||||
Icon,
|
||||
IconArrowDownSmall,
|
||||
IconCopy,
|
||||
IconEmail,
|
||||
IconEnvelope,
|
||||
IconGoogle,
|
||||
IconOutlook,
|
||||
IconOffice365,
|
||||
IconYahoo
|
||||
} from '../../../../base/icons';
|
||||
} from '../../../../base/icons/svg';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { Tooltip } from '../../../../base/tooltip';
|
||||
import { copyText } from '../../../../base/util/copyText.web';
|
||||
|
||||
type Props = {
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The encoded invitation subject.
|
||||
*/
|
||||
inviteSubject: string,
|
||||
inviteSubject: string;
|
||||
|
||||
/**
|
||||
* The encoded invitation text to be sent.
|
||||
*/
|
||||
inviteText: string,
|
||||
inviteText: string;
|
||||
|
||||
/**
|
||||
* The encoded no new-lines iOS invitation text to be sent on default mail.
|
||||
*/
|
||||
inviteTextiOS: string,
|
||||
inviteTextiOS: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function,
|
||||
};
|
||||
const useStyles = makeStyles()((theme: Theme) => {
|
||||
return {
|
||||
container: {
|
||||
marginTop: theme.spacing(4)
|
||||
},
|
||||
|
||||
label: {
|
||||
marginBottom: theme.spacing(2)
|
||||
},
|
||||
|
||||
iconRow: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
|
||||
iconContainer: {
|
||||
display: 'block',
|
||||
padding: theme.spacing(2),
|
||||
cursor: 'pointer',
|
||||
|
||||
'& svg>path': {
|
||||
fill: theme.palette.icon01
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Component that renders email invite options.
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
* @returns {ReactNode}
|
||||
*/
|
||||
function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS, t }: Props) {
|
||||
const [ isActive, setIsActive ] = useState(false);
|
||||
function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS }: IProps) {
|
||||
const { classes } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const encodedInviteSubject = encodeURIComponent(inviteSubject);
|
||||
const encodedInviteText = encodeURIComponent(inviteText);
|
||||
const encodedInviteTextiOS = encodeURIComponent(inviteTextiOS);
|
||||
|
@ -70,46 +93,23 @@ function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS, t }: P
|
|||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function _onCopyTextKeyPress(e) {
|
||||
function _onCopyTextKeyPress(e: React.KeyboardEvent) {
|
||||
if (e.key === ' ' || e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
copyText(inviteText);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the email invite drawer.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function _onToggleActiveState() {
|
||||
setIsActive(!isActive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the email invite drawer.
|
||||
*
|
||||
* @param {Object} e - The key event to handle.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function _onToggleActiveStateKeyPress(e) {
|
||||
if (e.key === ' ' || e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
setIsActive(!isActive);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders clickable elements that each open an email client
|
||||
* containing a conference invite.
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
* @returns {ReactNode}
|
||||
*/
|
||||
function renderEmailIcons() {
|
||||
const PROVIDER_MAPPING = [
|
||||
{
|
||||
icon: IconEmail,
|
||||
icon: IconEnvelope,
|
||||
tooltipKey: 'addPeople.defaultEmail',
|
||||
url: `mailto:?subject=${encodedInviteSubject}&body=${encodedDefaultEmailText}`
|
||||
},
|
||||
|
@ -119,7 +119,7 @@ function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS, t }: P
|
|||
url: `https://mail.google.com/mail/?view=cm&fs=1&su=${encodedInviteSubject}&body=${encodedInviteText}`
|
||||
},
|
||||
{
|
||||
icon: IconOutlook,
|
||||
icon: IconOffice365,
|
||||
tooltipKey: 'addPeople.outlookEmail',
|
||||
// eslint-disable-next-line max-len
|
||||
url: `https://outlook.office.com/mail/deeplink/compose?subject=${encodedInviteSubject}&body=${encodedInviteText}`
|
||||
|
@ -141,7 +141,7 @@ function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS, t }: P
|
|||
position = 'top'>
|
||||
<a
|
||||
aria-label = { t(tooltipKey) }
|
||||
className = 'provider-icon'
|
||||
className = { classes.iconContainer }
|
||||
href = { url }
|
||||
rel = 'noopener noreferrer'
|
||||
target = '_blank'>
|
||||
|
@ -157,26 +157,18 @@ function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS, t }: P
|
|||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div
|
||||
aria-expanded = { isActive }
|
||||
aria-label = { t('addPeople.shareInvite') }
|
||||
className = { `invite-more-dialog email-container${isActive ? ' active' : ''}` }
|
||||
onClick = { _onToggleActiveState }
|
||||
onKeyPress = { _onToggleActiveStateKeyPress }
|
||||
role = 'button'
|
||||
tabIndex = { 0 }>
|
||||
<span>{t('addPeople.shareInvite')}</span>
|
||||
<Icon src = { IconArrowDownSmall } />
|
||||
</div>
|
||||
<div className = { `invite-more-dialog icon-container${isActive ? ' active' : ''}` }>
|
||||
<div className = { classes.container }>
|
||||
<p className = { classes.label }>{t('addPeople.shareInvite')}</p>
|
||||
<div className = { classes.iconRow }>
|
||||
<Tooltip
|
||||
content = { t('addPeople.copyInvite') }
|
||||
position = 'top'>
|
||||
<div
|
||||
aria-label = { t('addPeople.copyInvite') }
|
||||
className = 'copy-invite-icon'
|
||||
className = { classes.iconContainer }
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick = { _onCopyText }
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onKeyPress = { _onCopyTextKeyPress }
|
||||
role = 'button'
|
||||
tabIndex = { 0 }>
|
||||
|
@ -190,4 +182,4 @@ function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS, t }: P
|
|||
);
|
||||
}
|
||||
|
||||
export default translate(InviteByEmailSection);
|
||||
export default InviteByEmailSection;
|
|
@ -24,7 +24,7 @@ const DEFAULT_STATE = {
|
|||
|
||||
export interface IInviteState {
|
||||
calleeInfoVisible?: boolean;
|
||||
conferenceID?: string;
|
||||
conferenceID?: string | number;
|
||||
error?: Error;
|
||||
initialCalleeInfo?: Object;
|
||||
numbers?: string;
|
||||
|
|