feat(tooltips): convert popup tooltips to InlineDialog

pull/1916/head
Leonard Kim 8 years ago committed by yanas
parent c3a4a38414
commit e3361e2f3b
  1. 12
      conference.js
  2. 4
      css/_toolbars.scss
  3. 4
      lang/main.json
  4. 22
      modules/UI/UI.js
  5. 3
      modules/UI/recording/Recording.js
  6. 6
      modules/UI/shared_video/SharedVideo.js
  7. 63
      react/features/toolbox/actions.web.js
  8. 92
      react/features/toolbox/components/ToolbarButton.web.js
  9. 20
      react/features/toolbox/components/Toolbox.web.js
  10. 7
      react/features/toolbox/defaultToolbarButtons.js
  11. 2
      react/features/toolbox/functions.web.js

@ -1263,21 +1263,24 @@ export default {
* @returns {void} * @returns {void}
*/ */
_displayAudioOnlyTooltip(featureName) { _displayAudioOnlyTooltip(featureName) {
let buttonName = null;
let tooltipElementId = null; let tooltipElementId = null;
switch (featureName) { switch (featureName) {
case 'screenShare': case 'screenShare':
tooltipElementId = '#screenshareWhileAudioOnly'; buttonName = 'desktop';
tooltipElementId = 'screenshareWhileAudioOnly';
break; break;
case 'videoMute': case 'videoMute':
tooltipElementId = '#unmuteWhileAudioOnly'; buttonName = 'camera';
tooltipElementId = 'unmuteWhileAudioOnly';
break; break;
} }
if (tooltipElementId) { if (tooltipElementId) {
APP.UI.showToolbar(6000); APP.UI.showToolbar(6000);
APP.UI.showCustomToolbarPopup( APP.UI.showCustomToolbarPopup(
tooltipElementId, true, 5000); buttonName, tooltipElementId, true, 5000);
} }
}, },
@ -1697,7 +1700,8 @@ export default {
room.on(ConferenceEvents.TALK_WHILE_MUTED, () => { room.on(ConferenceEvents.TALK_WHILE_MUTED, () => {
APP.UI.showToolbar(6000); APP.UI.showToolbar(6000);
APP.UI.showCustomToolbarPopup('#talkWhileMutedPopup', true, 5000); APP.UI.showCustomToolbarPopup(
'microphone', 'talkWhileMutedPopup', true, 5000);
}); });
room.on( room.on(

@ -130,11 +130,11 @@
@include transform(translateX(-50%)); @include transform(translateX(-50%));
.button:first-child { > div:first-child .button {
border-bottom-left-radius: 3px; border-bottom-left-radius: 3px;
border-top-left-radius: 3px; border-top-left-radius: 3px;
} }
.button:last-child { > div:last-child .button {
border-bottom-right-radius: 3px; border-bottom-right-radius: 3px;
border-top-right-radius: 3px; border-top-right-radius: 3px;
} }

@ -114,8 +114,8 @@
"login": "Login", "login": "Login",
"logout": "Logout", "logout": "Logout",
"dialpad": "Open / Close dialpad", "dialpad": "Open / Close dialpad",
"sharedVideoMutedPopup": "Your shared video has been muted so<br/>that you can talk to the other participants.", "sharedVideoMutedPopup": "Your shared video has been muted so that you can talk to the other participants.",
"micMutedPopup": "Your microphone has been muted so that you<br/>would fully enjoy your shared video.", "micMutedPopup": "Your microphone has been muted so that you would fully enjoy your shared video.",
"talkWhileMutedPopup": "Trying to speak? You are muted.", "talkWhileMutedPopup": "Trying to speak? You are muted.",
"unableToUnmutePopup": "You cannot un-mute while the shared video is on.", "unableToUnmutePopup": "You cannot un-mute while the shared video is on.",
"cameraDisabled": "Camera is not available", "cameraDisabled": "Camera is not available",

@ -30,7 +30,9 @@ import {
import { openDisplayNamePrompt } from '../../react/features/display-name'; import { openDisplayNamePrompt } from '../../react/features/display-name';
import { import {
checkAutoEnableDesktopSharing, checkAutoEnableDesktopSharing,
clearButtonPopup,
dockToolbox, dockToolbox,
setButtonPopupTimeout,
setToolbarButton, setToolbarButton,
showDialPadButton, showDialPadButton,
showEtherpadButton, showEtherpadButton,
@ -609,13 +611,21 @@ UI.inputDisplayNameHandler = function (newDisplayName) {
/** /**
* Show custom popup/tooltip for a specified button. * Show custom popup/tooltip for a specified button.
* @param popupSelectorID the selector id of the popup to show *
* @param show true or false/show or hide the popup * @param {string} buttonName - The name of the button as specified in the
* @param timeout the time to show the popup * button configurations for the toolbar.
* @param {string} popupSelectorID - The id of the popup to show as specified in
* the button configurations for the toolbar.
* @param {boolean} show - True or false/show or hide the popup
* @param {number} timeout - The time to show the popup
* @returns {void}
*/ */
UI.showCustomToolbarPopup = function (popupSelectorID, show, timeout) { UI.showCustomToolbarPopup = function (buttonName, popupID, show, timeout) {
eventEmitter.emit(UIEvents.SHOW_CUSTOM_TOOLBAR_BUTTON_POPUP, const action = show
popupSelectorID, show, timeout); ? setButtonPopupTimeout(buttonName, popupID, timeout)
: clearButtonPopup(buttonName);
APP.store.dispatch(action);
}; };
/** /**

@ -18,7 +18,6 @@ const logger = require("jitsi-meet-logger").getLogger(__filename);
import UIEvents from "../../../service/UI/UIEvents"; import UIEvents from "../../../service/UI/UIEvents";
import UIUtil from '../util/UIUtil'; import UIUtil from '../util/UIUtil';
import { setTooltip } from '../util/Tooltip';
import VideoLayout from '../videolayout/VideoLayout'; import VideoLayout from '../videolayout/VideoLayout';
import { setToolboxEnabled } from '../../../react/features/toolbox'; import { setToolboxEnabled } from '../../../react/features/toolbox';
@ -324,8 +323,6 @@ var Recording = {
initRecordingButton() { initRecordingButton() {
const selector = $('#toolbar_button_record'); const selector = $('#toolbar_button_record');
setTooltip(selector, 'liveStreaming.buttonTooltip', 'right');
selector.addClass(this.baseClass); selector.addClass(this.baseClass);
selector.attr("data-i18n", "[content]" + this.recordingButtonTooltip); selector.attr("data-i18n", "[content]" + this.recordingButtonTooltip);
APP.translation.translateElement(selector); APP.translation.translateElement(selector);

@ -558,7 +558,8 @@ export default class SharedVideoManager {
if(show) if(show)
this.showSharedVideoMutedPopup(false); this.showSharedVideoMutedPopup(false);
APP.UI.showCustomToolbarPopup('#micMutedPopup', show, 5000); APP.UI.showCustomToolbarPopup(
'microphone', 'micMutedPopup', show, 5000);
} }
/** /**
@ -571,7 +572,8 @@ export default class SharedVideoManager {
if(show) if(show)
this.showMicMutedPopup(false); this.showMicMutedPopup(false);
APP.UI.showCustomToolbarPopup('#sharedVideoMutedPopup', show, 5000); APP.UI.showCustomToolbarPopup(
'sharedvideo', 'sharedVideoMutedPopup', show, 5000);
} }
} }

@ -20,6 +20,7 @@ import {
} from './actions.native'; } from './actions.native';
import { SET_DEFAULT_TOOLBOX_BUTTONS } from './actionTypes'; import { SET_DEFAULT_TOOLBOX_BUTTONS } from './actionTypes';
import { import {
getButton,
getDefaultToolboxButtons, getDefaultToolboxButtons,
isButtonEnabled isButtonEnabled
} from './functions'; } from './functions';
@ -48,6 +49,23 @@ export function checkAutoEnableDesktopSharing(): Function {
}; };
} }
/**
* Dispatches an action to hide any popups displayed by the associated button.
*
* @param {string} buttonName - The name of the button as specified in the
* button configurations for the toolbar.
* @returns {Function}
*/
export function clearButtonPopup(buttonName) {
return (dispatch, getState) => {
_clearPopupTimeout(buttonName, getState());
dispatch(setToolbarButton(buttonName, {
popupDisplay: null
}));
};
}
/** /**
* Docks/undocks the Toolbox. * Docks/undocks the Toolbox.
* *
@ -195,6 +213,34 @@ export function hideToolbox(force: boolean = false): Function {
}; };
} }
/**
* Dispatches an action to show the popup associated with a button. Sets a
* timeout to be fired which will dismiss the popup.
*
* @param {string} buttonName - The name of the button as specified in the
* button configurations for the toolbar.
* @param {string} popupName - The id of the popup to show as specified in
* the button configurations for the toolbar.
* @param {number} timeout - The time in milliseconds to show the popup.
* @returns {Function}
*/
export function setButtonPopupTimeout(buttonName, popupName, timeout) {
return (dispatch, getState) => {
_clearPopupTimeout(buttonName, getState());
const newTimeoutId = setTimeout(() => {
dispatch(clearButtonPopup(buttonName));
}, timeout);
dispatch(setToolbarButton(buttonName, {
popupDisplay: {
popupID: popupName,
timeoutID: newTimeoutId
}
}));
};
}
/** /**
* Sets the default toolbar buttons of the Toolbox. * Sets the default toolbar buttons of the Toolbox.
* *
@ -387,3 +433,20 @@ export function toggleSideToolbarContainer(containerId: string): Function {
} }
}; };
} }
/**
* Clears the timeout set for hiding a button popup.
*
* @param {string} buttonName - The name of the button as specified in the
* button configurations for the toolbar.
* @param {Object} state - The redux state in which the button is expected to
* be defined.
* @private
* @returns {void}
*/
function _clearPopupTimeout(buttonName, state) {
const { popupDisplay } = getButton(buttonName, state);
const { timeoutID } = popupDisplay || {};
clearTimeout(timeoutID);
}

@ -1,5 +1,6 @@
/* @flow */ /* @flow */
import AKInlineDialog from '@atlaskit/inline-dialog';
import { Tooltip } from '@atlaskit/tooltip'; import { Tooltip } from '@atlaskit/tooltip';
import React, { Component } from 'react'; import React, { Component } from 'react';
@ -11,6 +12,18 @@ import StatelessToolbarButton from './StatelessToolbarButton';
declare var APP: Object; declare var APP: Object;
/**
* Mapping of tooltip positions to equivalent {@code AKInlineDialog} positions.
*
* @private
*/
const TOOLTIP_TO_POPUP_POSITION = {
bottom: 'bottom center',
left: 'left middle',
top: 'top center',
right: 'right middle'
};
/** /**
* Represents a button in Toolbar on React. * Represents a button in Toolbar on React.
* *
@ -127,26 +140,39 @@ class ToolbarButton extends Component {
*/ */
render(): ReactElement<*> { render(): ReactElement<*> {
const { button, t, tooltipPosition } = this.props; const { button, t, tooltipPosition } = this.props;
const popups = button.popups || [];
const props = { const props = {
...this.props, ...this.props,
onClick: this._onClick, onClick: this._onClick,
createRefToButton: this._createRefToButton createRefToButton: this._createRefToButton
}; };
return ( const buttonComponent = ( // eslint-disable-line no-extra-parens
<Tooltip <Tooltip
description = { button.tooltipText || t(button.tooltipKey) } description = { button.tooltipText || t(button.tooltipKey) }
onMouseOut = { this._onMouseOut } onMouseOut = { this._onMouseOut }
onMouseOver = { this._onMouseOver } onMouseOver = { this._onMouseOver }
position = { tooltipPosition } position = { tooltipPosition }
visible = { this.state.showTooltip }> visible = { this.state.showTooltip }>
<StatelessToolbarButton { ...props }> <StatelessToolbarButton { ...props } />
{ this._renderPopups(popups) }
</StatelessToolbarButton>
</Tooltip> </Tooltip>
); );
const popupConfig = this._getPopupDisplayConfiguration();
if (popupConfig) {
const { dataAttr, dataInterpolate, position } = popupConfig;
return (
<AKInlineDialog
content = { t(dataAttr, dataInterpolate) }
isOpen = { Boolean(popupConfig) }
position = { position }>
{ buttonComponent }
</AKInlineDialog>
);
}
return buttonComponent;
} }
/** /**
@ -174,46 +200,44 @@ class ToolbarButton extends Component {
} }
/** /**
* If toolbar button should contain children elements * Parses the props and state to find any popup that should be displayed
* renders them. * and returns an object describing how the popup should display.
* *
* @returns {ReactElement|null}
* @private * @private
* @returns {Object|null}
*/ */
_renderInnerElementsIfRequired(): ReactElement<*> | null { _getPopupDisplayConfiguration() {
if (this.props.button.html) { const { button, tooltipPosition } = this.props;
return this.props.button.html; const { popups, popupDisplay } = button;
if (!popups || !popupDisplay) {
return null;
} }
return null; const { popupID } = popupDisplay;
const currentPopup = popups.find(popup => popup.id === popupID);
return Object.assign(
{},
currentPopup || {},
{
position: TOOLTIP_TO_POPUP_POSITION[tooltipPosition]
});
} }
/** /**
* Renders popup element for toolbar button. * If toolbar button should contain children elements
* renders them.
* *
* @param {Array} popups - Array of popup objects. * @returns {ReactElement|null}
* @returns {Array}
* @private * @private
*/ */
_renderPopups(popups: Array<*> = []): Array<*> { _renderInnerElementsIfRequired(): ReactElement<*> | null {
return popups.map(popup => { if (this.props.button.html) {
let gravity = 'n'; return this.props.button.html;
}
if (popup.dataAttrPosition) {
gravity = popup.dataAttrPosition;
}
const title = this.props.t(popup.dataAttr, popup.dataInterpolate);
return ( return null;
<div
className = { popup.className }
data-popup = { gravity }
id = { popup.id }
key = { popup.id }
title = { title } />
);
});
} }
/** /**

@ -3,15 +3,12 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import UIEvents from '../../../../service/UI/UIEvents';
import { import {
setDefaultToolboxButtons, setDefaultToolboxButtons,
setToolboxAlwaysVisible setToolboxAlwaysVisible
} from '../actions'; } from '../actions';
import { import {
abstractMapStateToProps, abstractMapStateToProps
showCustomToolbarPopup
} from '../functions'; } from '../functions';
import Notice from './Notice'; import Notice from './Notice';
import PrimaryToolbar from './PrimaryToolbar'; import PrimaryToolbar from './PrimaryToolbar';
@ -71,10 +68,6 @@ class Toolbox extends Component {
componentDidMount(): void { componentDidMount(): void {
this.props._setToolboxAlwaysVisible(); this.props._setToolboxAlwaysVisible();
APP.UI.addListener(
UIEvents.SHOW_CUSTOM_TOOLBAR_BUTTON_POPUP,
showCustomToolbarPopup);
// FIXME The redux action SET_DEFAULT_TOOLBOX_BUTTONS and related source // FIXME The redux action SET_DEFAULT_TOOLBOX_BUTTONS and related source
// code such as the redux action creator setDefaultToolboxButtons and // code such as the redux action creator setDefaultToolboxButtons and
// _setDefaultToolboxButtons were introduced to solve the following bug // _setDefaultToolboxButtons were introduced to solve the following bug
@ -89,17 +82,6 @@ class Toolbox extends Component {
this.props._setDefaultToolboxButtons(); this.props._setDefaultToolboxButtons();
} }
/**
* Unregisters legacy UI listeners.
*
* @returns {void}
*/
componentWillUnmount(): void {
APP.UI.removeListener(
UIEvents.SHOW_CUSTOM_TOOLBAR_BUTTON_POPUP,
showCustomToolbarPopup);
}
/** /**
* Implements React's {@link Component#render()}. * Implements React's {@link Component#render()}.
* *

@ -51,7 +51,6 @@ const buttons: Object = {
}, },
popups: [ popups: [
{ {
className: 'loginmenu',
dataAttr: 'audioOnly.featureToggleDisabled', dataAttr: 'audioOnly.featureToggleDisabled',
dataInterpolate: { feature: 'video mute' }, dataInterpolate: { feature: 'video mute' },
id: 'unmuteWhileAudioOnly' id: 'unmuteWhileAudioOnly'
@ -143,7 +142,6 @@ const buttons: Object = {
}, },
popups: [ popups: [
{ {
className: 'loginmenu',
dataAttr: 'audioOnly.featureToggleDisabled', dataAttr: 'audioOnly.featureToggleDisabled',
dataInterpolate: { feature: 'screen sharing' }, dataInterpolate: { feature: 'screen sharing' },
id: 'screenshareWhileAudioOnly' id: 'screenshareWhileAudioOnly'
@ -313,17 +311,14 @@ const buttons: Object = {
}, },
popups: [ popups: [
{ {
className: 'loginmenu',
dataAttr: 'toolbar.micMutedPopup', dataAttr: 'toolbar.micMutedPopup',
id: 'micMutedPopup' id: 'micMutedPopup'
}, },
{ {
className: 'loginmenu',
dataAttr: 'toolbar.unableToUnmutePopup', dataAttr: 'toolbar.unableToUnmutePopup',
id: 'unableToUnmutePopup' id: 'unableToUnmutePopup'
}, },
{ {
className: 'loginmenu',
dataAttr: 'toolbar.talkWhileMutedPopup', dataAttr: 'toolbar.talkWhileMutedPopup',
id: 'talkWhileMutedPopup' id: 'talkWhileMutedPopup'
} }
@ -419,9 +414,7 @@ const buttons: Object = {
}, },
popups: [ popups: [
{ {
className: 'loginmenu extendedToolbarPopup',
dataAttr: 'toolbar.sharedVideoMutedPopup', dataAttr: 'toolbar.sharedVideoMutedPopup',
dataAttrPosition: 'w',
id: 'sharedVideoMutedPopup' id: 'sharedVideoMutedPopup'
} }
], ],

@ -7,7 +7,7 @@ declare var $: Function;
declare var AJS: Object; declare var AJS: Object;
declare var interfaceConfig: Object; declare var interfaceConfig: Object;
export { abstractMapStateToProps } from './functions.native'; export { abstractMapStateToProps, getButton } from './functions.native';
/** /**
* Returns an object which contains the default buttons for the primary and * Returns an object which contains the default buttons for the primary and

Loading…
Cancel
Save