Info dialog: bold labels, no url truncation, only auto show on lonely call (#2619)

* fix(info): bold info labels

* fix(info): do not truncate url

* feat(info): show only during a lonely call
This commit is contained in:
virtuacoplenny 2018-04-04 08:58:54 -07:00 committed by bbaldino
parent 2412239206
commit 2f23f8e400
10 changed files with 119 additions and 175 deletions

View File

@ -38,7 +38,7 @@
.info-dialog-copy-element {
opacity: 0;
pointer-events: none;
position: fixed;
position: absolute;
-webkit-user-select: text;
user-select: text;
}
@ -56,11 +56,11 @@
}
.info-dialog-conference-url {
max-width: 250px;
overflow: hidden;
text-overflow: ellipsis;
user-select: text;
white-space: nowrap;
width: max-content;
width: -moz-max-content;
width: -webkit-max-content;
word-break: break-all;
max-width: 400px;
}
.info-dialog-dial-in {
@ -82,14 +82,18 @@
margin-bottom: 10px;
}
.info-password,
.info-dialog-password,
.info-password,
.info-password-form {
align-items: baseline;
display: flex;
}
.info-label {
font-weight: bold;
}
.info-password-field {
margin-left: 2px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@ -110,10 +114,6 @@
.info-password-local {
user-select: text;
}
.conference-id {
margin-left: 5px;
}
}
.dial-in-page {
@ -125,10 +125,6 @@
justify-content: center;
width: 100%;
* {
user-select: text;
}
.dial-in-numbers-list {
font-size: 24px;
margin-top: 20px;
@ -139,3 +135,12 @@
width: 30%;
}
}
.info-dialog,
.dial-in-page {
* {
user-select: text;
-moz-user-select: text;
-webkit-user-select: text;
}
}

View File

@ -516,11 +516,11 @@
"info": {
"addPassword": "Add password",
"cancelPassword": "Cancel password",
"conferenceURL": "Link: __url__",
"conferenceURL": "Link:",
"country": "Country",
"dialANumber": "To join your meeting, dial one of these numbers and then enter this PIN: __conferenceID__#",
"dialInNumber": "Dial-in: __phoneNumber__",
"dialInConferenceID": "PIN: __conferenceID__#",
"dialInNumber": "Dial-in:",
"dialInConferenceID": "PIN:",
"dialInNotSupported": "Sorry, dialing in is currently not suppported.",
"genericError": "Whoops, something went wrong.",
"invitePhone": "To join by phone, dial __number__ and enter this PIN: __conferenceID__#",
@ -532,7 +532,7 @@
"noRoom": "No room was specified to dial-in into.",
"numbers": "Dial-in Numbers",
"password": "Password:",
"title": "Call info",
"title": "Share",
"tooltip": "Get access info about the meeting"
},
"settingsView": {

View File

@ -1,14 +1,3 @@
/**
* The type of the action which signals a request to display the inline
* conference info dialog.
*
* {
* type: SET_INFO_DIALOG_VISIBILITY,
* visible: boolean
* }
*/
export const SET_INFO_DIALOG_VISIBILITY = Symbol('SET_INFO_DIALOG_VISIBILITY');
/**
* The type of the action which signals an error occurred while requesting dial-
* in numbers.

View File

@ -1,34 +1,11 @@
// @flow
import {
SET_INFO_DIALOG_VISIBILITY,
UPDATE_DIAL_IN_NUMBERS_FAILED,
UPDATE_DIAL_IN_NUMBERS_SUCCESS
} from './actionTypes';
import { getDialInConferenceID, getDialInNumbers } from './functions';
/**
* Opens the inline conference info dialog.
*
* @param {boolean} visible - Whether or not the dialog should be displayed.
* @param {boolean} autoClose - Whether or not the dialog should automatically
* close after a set period of time.
* @returns {{
* type: SET_INFO_DIALOG_VISIBILITY,
* autoClose: boolean,
* visible: boolean
* }}
*/
export function setInfoDialogVisibility(
visible: boolean,
autoClose: boolean = false) {
return {
type: SET_INFO_DIALOG_VISIBILITY,
autoClose,
visible
};
}
/**
* Sends AJAX requests for dial-in numbers and conference ID.
*

View File

@ -7,16 +7,16 @@ import { connect } from 'react-redux';
import { createToolbarEvent, sendAnalytics } from '../../analytics';
import { translate } from '../../base/i18n';
import { getParticipantCount } from '../../base/participants';
import {
ToolbarButton,
ToolbarButtonV2,
TOOLTIP_TO_POPUP_POSITION
} from '../../toolbox';
import { setInfoDialogVisibility, updateDialInNumbers } from '../actions';
import { InfoDialog } from './info-dialog';
import { updateDialInNumbers } from '../actions';
const { INITIAL_TOOLBAR_TIMEOUT } = interfaceConfig;
import { InfoDialog } from './info-dialog';
/**
* A configuration object to describe how {@code ToolbarButton} should render
@ -32,6 +32,14 @@ const DEFAULT_BUTTON_CONFIGURATION = {
tooltipKey: 'info.tooltip'
};
/**
* The amount of time, in milliseconds, to wait until automatically showing
* the {@code InfoDialog}. This is essentially a hack as automatic showing
* should happen in a lonely call and some time is needed to populate
* participants already in the call.
*/
const INFO_DIALOG_AUTO_SHOW_TIMEOUT = 1500;
/**
* A React Component for displaying a button which opens a dialog with
* information about the conference and with ways to invite people.
@ -55,15 +63,16 @@ class InfoDialogButton extends Component {
]),
/**
* Whether or not the {@code InfoDialog} should close by itself after a
* a timeout.
* Whether or not the {@code InfoDialog} should display automatically
* after {@link INFO_DIALOG_AUTO_SHOW_TIMEOUT}.
*/
_shouldAutoClose: PropTypes.bool,
_disableAutoShow: PropTypes.bool,
/**
* Whether or not {@code InfoDialog} should be displayed.
* The number of real participants in the call. If in a lonely call,
* the {@code InfoDialog} will be automatically shown.
*/
_showDialog: PropTypes.bool,
_participantCount: PropTypes.number,
/**
* Whether or not the toolbox, in which this component exists, are
@ -98,16 +107,23 @@ class InfoDialogButton extends Component {
super(props);
/**
* The timeout to automatically hide the {@code InfoDialog} if it has
* not been interacted with.
* The timeout to automatically show the {@code InfoDialog} if it has
* not been shown yet in a lonely call.
*
* @type {timeoutID}
*/
this._autoHideDialogTimeout = null;
this._autoShowTimeout = null;
this.state = {
/**
* Whether or not {@code InfoDialog} should be visible.
*/
showDialog: false
};
// Bind event handlers so they are only bound once for every instance.
this._onDialogClose = this._onDialogClose.bind(this);
this._onDialogMouseOver = this._onDialogMouseOver.bind(this);
this._onDialogToggle = this._onDialogToggle.bind(this);
}
@ -117,30 +133,15 @@ class InfoDialogButton extends Component {
* @inheritdoc
*/
componentDidMount() {
if (this.props._shouldAutoClose) {
this._setAutoCloseTimeout();
}
this._autoShowTimeout = setTimeout(() => {
this._maybeAutoShowDialog();
}, INFO_DIALOG_AUTO_SHOW_TIMEOUT);
if (!this.props._dialInNumbers) {
this.props.dispatch(updateDialInNumbers());
}
}
/**
* Set or clear the timeout to automatically hide the {@code InfoDialog}.
*
* @inheritdoc
*/
componentDidUpdate(prevProps) {
// If the _shouldAutoClose flag has been updated to be true then make
// sure to set _autoHideDialogTimeout.
if (this.props._shouldAutoClose && !prevProps._shouldAutoClose) {
this._setAutoCloseTimeout();
} else {
this._clearAutoCloseTimeout();
}
}
/**
* Update the visibility of the {@code InfoDialog}.
*
@ -148,7 +149,7 @@ class InfoDialogButton extends Component {
*/
componentWillReceiveProps(nextProps) {
// Ensure the dialog is closed when the toolbox becomes hidden.
if (nextProps._showDialog && !nextProps._toolboxVisible) {
if (this.state.showDialog && !nextProps._toolboxVisible) {
this._onDialogClose();
}
}
@ -159,7 +160,7 @@ class InfoDialogButton extends Component {
* @inheritdoc
*/
componentWillUnmount() {
this._clearAutoCloseTimeout();
clearTimeout(this._autoShowTimeout);
}
/**
@ -175,14 +176,16 @@ class InfoDialogButton extends Component {
}
/**
* Cancels the timeout to automatically hide the {@code InfoDialog}.
* Callback invoked after a timeout to trigger display of the
* {@code InfoDialog} if certain conditions are met.
*
* @private
* @returns {void}
*/
_clearAutoCloseTimeout() {
clearTimeout(this._autoHideDialogTimeout);
this._autoHideDialogTimeout = null;
_maybeAutoShowDialog() {
if (this.props._participantCount < 2 && !this.props._disableAutoShow) {
this.setState({ showDialog: true });
}
}
/**
@ -192,17 +195,7 @@ class InfoDialogButton extends Component {
* @returns {void}
*/
_onDialogClose() {
this.props.dispatch(setInfoDialogVisibility(false));
}
/**
* Cancels the timeout to automatically hide the {@code InfoDialog}.
*
* @private
* @returns {void}
*/
_onDialogMouseOver() {
this._clearAutoCloseTimeout();
this.setState({ showDialog: false });
}
/**
@ -214,7 +207,7 @@ class InfoDialogButton extends Component {
_onDialogToggle() {
sendAnalytics(createToolbarEvent('info'));
this.props.dispatch(setInfoDialogVisibility(!this.props._showDialog));
this.setState({ showDialog: !this.state.showDialog });
}
/**
@ -225,22 +218,21 @@ class InfoDialogButton extends Component {
* @returns {ReactElement}
*/
_renderOldToolbarButton() {
const { _showDialog, _toolboxVisible, tooltipPosition } = this.props;
const { tooltipPosition } = this.props;
const { showDialog } = this.state;
const buttonConfiguration = {
...DEFAULT_BUTTON_CONFIGURATION,
classNames: [
...DEFAULT_BUTTON_CONFIGURATION.classNames,
_showDialog ? 'toggled button-active' : ''
showDialog ? 'toggled button-active' : ''
]
};
return (
<InlineDialog
content = { <InfoDialog
autoUpdateNumbers = { false }
onClose = { this._onDialogClose }
onMouseOver = { this._onDialogMouseOver } /> }
isOpen = { _toolboxVisible && _showDialog }
content = { <InfoDialog onClose = { this._onDialogClose } /> }
isOpen = { showDialog }
onClose = { this._onDialogClose }
position = { TOOLTIP_TO_POPUP_POSITION[tooltipPosition] }>
<ToolbarButton
@ -259,17 +251,16 @@ class InfoDialogButton extends Component {
* @returns {ReactElement}
*/
_renderNewToolbarButton() {
const { _showDialog, _toolboxVisible, t } = this.props;
const iconClass = `icon-info ${_showDialog ? 'toggled' : ''}`;
const { t } = this.props;
const { showDialog } = this.state;
const iconClass = `icon-info ${showDialog ? 'toggled' : ''}`;
return (
<div className = 'toolbox-button-wth-dialog'>
<InlineDialog
content = { <InfoDialog
autoUpdateNumbers = { false }
onClose = { this._onDialogClose }
onMouseOver = { this._onDialogMouseOver } /> }
isOpen = { _toolboxVisible && _showDialog }
content = {
<InfoDialog onClose = { this._onDialogClose } /> }
isOpen = { showDialog }
onClose = { this._onDialogClose }
position = { 'top right' }>
<ToolbarButtonV2
@ -281,22 +272,6 @@ class InfoDialogButton extends Component {
</div>
);
}
/**
* Set a timeout to automatically hide the {@code InfoDialog}.
*
* @private
* @returns {void}
*/
_setAutoCloseTimeout() {
this._clearAutoCloseTimeout();
this._autoHideDialogTimeout = setTimeout(() => {
if (this.props._showDialog) {
this._onDialogClose();
}
}, INITIAL_TOOLBAR_TIMEOUT);
}
}
/**
@ -307,22 +282,17 @@ class InfoDialogButton extends Component {
* @private
* @returns {{
* _dialInNumbers: Array,
* _shouldAutoClose: boolean,
* _showDialog: boolean,
* _disableAutoShow: bolean,
* _participantCount: number,
* _toolboxVisible: boolean
* }}
*/
function _mapStateToProps(state) {
const {
infoDialogVisible,
infoDialogWillAutoClose,
numbers
} = state['features/invite'];
return {
_dialInNumbers: numbers,
_shouldAutoClose: infoDialogWillAutoClose,
_showDialog: infoDialogVisible,
_dialInNumbers: state['features/invite'].numbers,
_disableAutoShow: state['features/base/config'].iAmRecorder,
_participantCount:
getParticipantCount(state['features/base/participants']),
_toolboxVisible: state['features/toolbox'].visible
};
}

View File

@ -41,16 +41,28 @@ class DialInNumber extends Component {
* @returns {ReactElement}
*/
render() {
const { conferenceID, phoneNumber } = this.props;
const { conferenceID, phoneNumber, t } = this.props;
return (
<div className = 'dial-in-number'>
<span className = 'phone-number'>
{ this.props.t('info.dialInNumber', { phoneNumber }) }
<span className = 'info-label'>
{ t('info.dialInNumber') }
</span>
<span className = 'spacer'>&nbsp;</span>
<span className = 'info-value'>
{ phoneNumber }
</span>
</span>
<span className = 'spacer'>&nbsp;</span>
<span className = 'conference-id'>
{ this.props.t(
'info.dialInConferenceID', { conferenceID }) }
<span className = 'info-label'>
{ t('info.dialInConferenceID') }
</span>
<span className = 'spacer'>&nbsp;</span>
<span className = 'info-value'>
{ `${conferenceID}#` }
</span>
</span>
</div>
);

View File

@ -214,14 +214,13 @@ class InfoDialog extends Component {
{ t('info.title') }
</div>
<div className = 'info-dialog-conference-url'>
{ t('info.conferenceURL',
{ url: this._getURLToDisplay() }) }
<textarea
className = 'info-dialog-copy-element'
readOnly = { true }
ref = { this._setCopyElement }
tabIndex = '-1'
value = { this._getTextToCopy() } />
<span className = 'info-label'>
{ t('info.conferenceURL') }
</span>
<span className = 'spacer'>&nbsp;</span>
<span className = 'info-value'>
{ this._getURLToDisplay() }
</span>
</div>
<div className = 'info-dialog-dial-in'>
{ this._renderDialInDisplay() }
@ -244,6 +243,12 @@ class InfoDialog extends Component {
{ this._renderPasswordAction() }
</div>
</div>
<textarea
className = 'info-dialog-copy-element'
readOnly = { true }
ref = { this._setCopyElement }
tabIndex = '-1'
value = { this._getTextToCopy() } />
</div>
);
}

View File

@ -94,10 +94,13 @@ class PasswordForm extends Component {
return (
<div className = 'info-password'>
<div>{ t('info.password') }</div>
<div className = 'info-password-field'>
<span className = 'info-label'>
{ t('info.password') }
</span>
<span className = 'spacer'>&nbsp;</span>
<span className = 'info-password-field info-value'>
{ this._renderPasswordField() }
</div>
</span>
</div>
);
}

View File

@ -1,7 +1,5 @@
import { CONFERENCE_JOINED } from '../base/conference';
import { MiddlewareRegistry } from '../base/redux';
import { setInfoDialogVisibility } from './actions';
import { UPDATE_DIAL_IN_NUMBERS_FAILED } from './actionTypes';
const logger = require('jitsi-meet-logger').getLogger(__filename);
@ -17,13 +15,6 @@ MiddlewareRegistry.register(store => next => action => {
const result = next(action);
switch (action.type) {
case CONFERENCE_JOINED:
// we do not want to show call info in iAmRecorder mode
if (store.getState()['features/base/config'].iAmRecorder) {
return result;
}
store.dispatch(setInfoDialogVisibility(true, true));
break;
case UPDATE_DIAL_IN_NUMBERS_FAILED:
logger.error(

View File

@ -1,7 +1,6 @@
import { ReducerRegistry } from '../base/redux';
import {
SET_INFO_DIALOG_VISIBILITY,
UPDATE_DIAL_IN_NUMBERS_FAILED,
UPDATE_DIAL_IN_NUMBERS_SUCCESS
} from './actionTypes';
@ -12,13 +11,6 @@ const DEFAULT_STATE = {
ReducerRegistry.register('features/invite', (state = DEFAULT_STATE, action) => {
switch (action.type) {
case SET_INFO_DIALOG_VISIBILITY:
return {
...state,
infoDialogVisible: action.visible,
infoDialogWillAutoClose: action.autoClose
};
case UPDATE_DIAL_IN_NUMBERS_FAILED:
return {
...state,