diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/styles.scss b/bigbluebutton-html5/imports/ui/components/actions-bar/styles.scss index 5abb5ba57052801eddefe421d197374aad621f8e..4aa485dd4231b76a85cab34a97918feac94412f2 100644 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/styles.scss @@ -44,9 +44,3 @@ box-shadow: 0 2px 5px 0 rgb(0, 0, 0); } } - -.emojiSelected { - span, i { - color: $color-primary; - } -} diff --git a/bigbluebutton-html5/imports/ui/components/dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/dropdown/component.jsx index 5b1d110418052e9bcf2892f21e87ec8739f715cc..5af33405ae98bc3fd10de7ccc688b8f064e58d7e 100644 --- a/bigbluebutton-html5/imports/ui/components/dropdown/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/dropdown/component.jsx @@ -67,13 +67,14 @@ class Dropdown extends Component { this.handleHide = this.handleHide.bind(this); this.handleToggle = this.handleToggle.bind(this); this.handleWindowClick = this.handleWindowClick.bind(this); + this.componentDidUpdate = this.componentDidUpdate.bind(this); } componentWillUpdate(nextProps, nextState) { return nextState.isOpen ? screenreaderTrap.trap(this.dropdown) : screenreaderTrap.untrap(); } - componentDidUpdate(prevProps, prevState) { + componentDidUpdate(prevProps, prevState, event) { const { userDropdownOpen, closeUserDropdown, @@ -82,6 +83,15 @@ class Dropdown extends Component { onHide, } = this.props; + if (event) { + const menuContent = findDOMNode(this.content); + + if (!menuContent.contains(event.target)) { + this.props.emojiSelected(); + this.handleHide(); + } + } + if (userDropdownOpen) return closeUserDropdown(); const emojisToggled = (showEmojiMenu && !prevProps.showEmojiMenu) || (!showEmojiMenu && prevProps.showEmojiMenu); @@ -99,11 +109,13 @@ class Dropdown extends Component { }); } - handleHide() { + handleHide(event) { this.setState({ isOpen: false }, () => { const { removeEventListener } = window; removeEventListener('click', this.handleWindowClick, true); }); + + this.componentDidUpdate(this.props, this.state, event); } handleWindowClick(event) { @@ -117,7 +129,7 @@ class Dropdown extends Component { return; } - this.handleHide(); + this.handleHide(event); } handleToggle() { diff --git a/bigbluebutton-html5/imports/ui/components/user-list/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/component.jsx index 55089d965805382029839608aaab38742bad58c9..5937ce0dfec54835965976b038eada7acea5bc3c 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/component.jsx @@ -63,6 +63,8 @@ class UserList extends Component { roving, CustomLogoUrl, handleEmojiChange, + getEmojiList, + getEmoji, } = this.props; return ( @@ -93,6 +95,8 @@ class UserList extends Component { isPublicChat, roving, handleEmojiChange, + getEmojiList, + getEmoji, } } />} diff --git a/bigbluebutton-html5/imports/ui/components/user-list/container.jsx b/bigbluebutton-html5/imports/ui/components/user-list/container.jsx index d0401501d9b1728c24844ca9f6d997326ea22b03..8ab9a238ebd1127c544d45da62d1ee26593677e3 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/container.jsx @@ -47,4 +47,6 @@ export default withTracker(({ chatID, compact }) => ({ CustomLogoUrl: Service.getCustomLogoUrl(), compact, handleEmojiChange: Service.setEmojiStatus, + getEmojiList: Service.getEmojiList(), + getEmoji: Service.getEmoji(), }))(UserListContainer); diff --git a/bigbluebutton-html5/imports/ui/components/user-list/service.js b/bigbluebutton-html5/imports/ui/components/user-list/service.js index ea8e000ae3c8db53391e103a3a271e8853266e9e..133926fa3ffdef29cd45b7d2eed7fe4268856502 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/service.js +++ b/bigbluebutton-html5/imports/ui/components/user-list/service.js @@ -322,25 +322,11 @@ const isMeetingLocked = (id) => { }; const setEmojiStatus = (s) => { - switch (s) { - case Auth.userId: - makeCall('setEmojiStatus', Auth.userID, 'none'); - break; - case 'away': - case 'hand': - case 'undecided': - case 'confused': - case 'sad': - case 'happy': - case 'applause': - case 'thumbs_up': - case 'thumbs_down': - makeCall('setEmojiStatus', Auth.userID, s); - break; - default: - makeCall('setEmojiStatus', s, 'none'); - break; - } + const statusAvailable = (Object.keys(EMOJI_STATUSES).includes(s)); + + return statusAvailable + ? makeCall('setEmojiStatus', Auth.userID, s) + : makeCall('setEmojiStatus', s, 'none'); }; const assignPresenter = (userId) => { makeCall('assignPresenter', userId); }; @@ -432,4 +418,6 @@ export default { roving, setCustomLogoUrl, getCustomLogoUrl, + getEmojiList: () => EMOJI_STATUSES, + getEmoji: () => Users.findOne({ userId: Auth.userID }).emoji, }; diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx index 44c574a91aed350e4faf20f9591f5dac0be07f26..790956c615d968def239534dad12293e6b8ab97c 100644 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx @@ -66,6 +66,8 @@ class UserContent extends Component { isMeetingLocked={this.props.isMeetingLocked} roving={this.props.roving} handleEmojiChange={this.props.handleEmojiChange} + getEmojiList={this.props.getEmojiList} + getEmoji={this.props.getEmoji} /> </div> ); diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx index ccd55106b6939c3178e160a969b86917ebeed6b6..8a3cba04a82fab993250fa671f852c44ee5ddb79 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/component.jsx @@ -143,6 +143,8 @@ class UserParticipants extends Component { removeUser, toggleVoice, handleEmojiChange, + getEmojiList, + getEmoji, } = this.props; const userActions = @@ -170,12 +172,12 @@ class UserParticipants extends Component { mute: { label: () => intl.formatMessage(intlMessages.MuteUserAudioLabel), handler: user => toggleVoice(user.id), - icon: 'audio_off', + icon: 'mute', }, unmute: { label: () => intl.formatMessage(intlMessages.UnmuteUserAudioLabel), handler: user => toggleVoice(user.id), - icon: 'audio_on', + icon: 'unmute', }, promote: { label: user => intl.formatMessage(intlMessages.PromoteUserLabel, { 0: user.name }), @@ -215,6 +217,8 @@ class UserParticipants extends Component { isMeetingLocked={isMeetingLocked} getScrollContainerRef={this.getScrollContainerRef} handleEmojiChange={handleEmojiChange} + getEmojiList={getEmojiList} + getEmoji={getEmoji} /> </div> </CSSTransition> diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/component.jsx index a161a94935a2afe3f353935a379c516c9081b71c..56c3243fc97ea6ef55d46c98f66e119fb0bf7534 100644 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/component.jsx @@ -7,6 +7,8 @@ import { defineMessages } from 'react-intl'; import DropdownListSeparator from '/imports/ui/components/dropdown/list/separator/component'; import UserListContent from './user-list-content/component'; import UserAction from './user-action/component'; +import DropdownListItem from '/imports/ui/components/dropdown/list/item/component'; +import { styles } from './styles'; const propTypes = { user: PropTypes.shape({ @@ -48,54 +50,14 @@ const intlMessages = defineMessages({ id: 'app.audio.backLabel', description: 'label for option to hide emoji menu', }, - awayLabel: { - id: 'app.actionsBar.emojiMenu.awayLabel', - description: 'label for the away emoji option', - }, - raiseHandLabel: { - id: 'app.actionsBar.emojiMenu.raiseHandLabel', - description: 'label for the raise hand emoji option', - }, - neutralLabel: { - id: 'app.actionsBar.emojiMenu.neutralLabel', - description: 'label for the undecided emoji option', - }, - confusedLabel: { - id: 'app.actionsBar.emojiMenu.confusedLabel', - description: 'label for the confused emoji option', - }, - sadLabel: { - id: 'app.actionsBar.emojiMenu.sadLabel', - description: 'label for the sad emoji option', - }, - happyLabel: { - id: 'app.actionsBar.emojiMenu.happyLabel', - description: 'label for the happy emoji option', - }, - applauseLabel: { - id: 'app.actionsBar.emojiMenu.applauseLabel', - description: 'label for the applause emoji option', - }, - thumbsUpLabel: { - id: 'app.actionsBar.emojiMenu.thumbsUpLabel', - description: 'label for the thumbs up emoji option', - }, - thumbsDownLabel: { - id: 'app.actionsBar.emojiMenu.thumbsDownLabel', - description: 'label for the thumbs down emoji option', - }, }); class UserListItem extends Component { static createAction(action, ...options) { - let icon = action.icon; - if (action.icon === 'audio_on') icon = 'unmute'; - if (action.icon === 'audio_off') icon = 'mute'; - return ( <UserAction key={_.uniqueId('action-item-')} - icon={icon} + icon={action.icon} label={action.label(...options)} handler={action.handler} options={[...options]} @@ -144,6 +106,8 @@ class UserListItem extends Component { isBreakoutRoom, getAvailableActions, handleEmojiChange, + getEmojiList, + getEmoji, } = this.props; const { @@ -172,7 +136,7 @@ class UserListItem extends Component { allowedToChangeStatus, } = actions; - const emojiRelatedOptions = { + const emojiMenuControls = { setstatus: { icon: 'right_arrow', label: () => intl.formatMessage(intlMessages.statusTriggerLabel), @@ -183,71 +147,30 @@ class UserListItem extends Component { label: () => intl.formatMessage(intlMessages.backTriggerLabel), handler: () => this.setState({ showEmojiMenu: false }), }, - away: { - icon: 'time', - label: () => intl.formatMessage(intlMessages.awayLabel), - handler: () => { handleEmojiChange('away'); this.emojiSelected(); }, - }, - raisehand: { - icon: 'hand', - label: () => intl.formatMessage(intlMessages.raiseHandLabel), - handler: () => { handleEmojiChange('hand'); this.emojiSelected(); }, - }, - neutral: { - icon: 'undecided', - label: () => intl.formatMessage(intlMessages.neutralLabel), - handler: () => { handleEmojiChange('undecided'); this.emojiSelected(); }, - }, - confused: { - icon: 'confused', - label: () => intl.formatMessage(intlMessages.confusedLabel), - handler: () => { handleEmojiChange('confused'); this.emojiSelected(); }, - }, - sad: { - icon: 'sad', - label: () => intl.formatMessage(intlMessages.sadLabel), - handler: () => { handleEmojiChange('sad'); this.emojiSelected(); }, - }, - happy: { - icon: 'happy', - label: () => intl.formatMessage(intlMessages.happyLabel), - handler: () => { handleEmojiChange('happy'); this.emojiSelected(); }, - }, - applause: { - icon: 'applause', - label: () => intl.formatMessage(intlMessages.applauseLabel), - handler: () => { handleEmojiChange('applause'); this.emojiSelected(); }, - }, - thumbsUp: { - icon: 'thumbs_up', - label: () => intl.formatMessage(intlMessages.thumbsUpLabel), - handler: () => { handleEmojiChange('thumbs_up'); this.emojiSelected(); }, - }, - thumbsDown: { - icon: 'thumbs_down', - label: () => intl.formatMessage(intlMessages.thumbsDownLabel), - handler: () => { handleEmojiChange('thumbs_down'); this.emojiSelected(); }, - }, }; if (this.state.showEmojiMenu) { + const statuses = Object.keys(getEmojiList); + const options = statuses.map(status => ( + <DropdownListItem + key={status} + className={status === getEmoji ? styles.emojiSelected : null} + icon={getEmojiList[status]} + label={intl.formatMessage({ id: `app.actionsBar.emojiMenu.${status}Label` })} + description={intl.formatMessage({ id: `app.actionsBar.emojiMenu.${status}Desc` })} + onClick={() => { handleEmojiChange(status); this.emojiSelected(); }} + tabIndex={-1} + /> + )); + return _.compact([ - (allowedToChangeStatus ? UserListItem.createAction(emojiRelatedOptions.back, user) : null), + (allowedToChangeStatus ? UserListItem.createAction(emojiMenuControls.back, user) : null), (<DropdownListSeparator key={_.uniqueId('list-separator-')} />), - (UserListItem.createAction(emojiRelatedOptions.away, user)), - (UserListItem.createAction(emojiRelatedOptions.raisehand, user)), - (UserListItem.createAction(emojiRelatedOptions.neutral, user)), - (UserListItem.createAction(emojiRelatedOptions.confused, user)), - (UserListItem.createAction(emojiRelatedOptions.sad, user)), - (UserListItem.createAction(emojiRelatedOptions.happy, user)), - (UserListItem.createAction(emojiRelatedOptions.applause, user)), - (UserListItem.createAction(emojiRelatedOptions.thumbsUp, user)), - (UserListItem.createAction(emojiRelatedOptions.thumbsDown, user)), - ]); + ]).concat(options); } return _.compact([ - (allowedToChangeStatus ? UserListItem.createAction(emojiRelatedOptions.setstatus, user) : null), + (allowedToChangeStatus ? UserListItem.createAction(emojiMenuControls.setstatus, user) : null), (allowedToChatPrivately ? UserListItem.createAction(openChat, router, user) : null), (allowedToMuteAudio ? UserListItem.createAction(mute, user) : null), (allowedToUnmuteAudio ? UserListItem.createAction(unmute, user) : null), @@ -285,6 +208,7 @@ class UserListItem extends Component { userDropdownOpen={this.state.userDropdownOpen} closeUserDropdown={this.closeUserDropdown} closeEmojiMenu={this.closeEmojiMenu} + emojiSelected={this.emojiSelected} />); return contents; diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/styles.scss b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/styles.scss new file mode 100644 index 0000000000000000000000000000000000000000..a066109c90969ab3185f9ff13b1d1d6875815dc9 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/styles.scss @@ -0,0 +1,7 @@ +@import "/imports/ui/stylesheets/variables/_all"; + +.emojiSelected { + span, i { + color: $color-primary; + } + } diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-list-content/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-list-content/component.jsx index 2cc00eb39258d4b0f3befd48d8236e293a546d59..2a63ae797f6af74c8ee8e92b94c9cf814c20f3df 100644 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-list-content/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/user-list-content/component.jsx @@ -81,6 +81,23 @@ class UserListContent extends Component { this.onActionsShow = this.onActionsShow.bind(this); this.onActionsHide = this.onActionsHide.bind(this); this.getDropdownMenuParent = this.getDropdownMenuParent.bind(this); + this.handleClick = this.handleClick.bind(this); + } + + handleClick() { + if (this.props.showEmojiMenu) { + this.props.emojiSelected(); + } + } + + componentDidMount() { + const triggerElement = findDOMNode(this.trigger); + triggerElement.addEventListener('click', this.handleClick, true); + } + + componentWillUnmount() { + const triggerElement = findDOMNode(this.trigger); + triggerElement.removeEventListener('click', this.handleWindowClick, true); } componentWillMount() { @@ -216,6 +233,7 @@ class UserListContent extends Component { const contents = ( <div className={!actions.length ? styles.userListItem : null} + ref={node => this.trigger = node} > <div className={styles.userItemContents}> <div className={styles.userAvatar}> @@ -259,6 +277,7 @@ class UserListContent extends Component { userDropdownOpen={this.props.userDropdownOpen} closeUserDropdown={this.props.closeUserDropdown} showEmojiMenu={this.props.showEmojiMenu} + emojiSelected={this.props.emojiSelected} isOpen={this.state.isActionsOpen} onShow={this.onActionsShow} onHide={this.onActionsHide} diff --git a/bigbluebutton-html5/imports/utils/statuses.js b/bigbluebutton-html5/imports/utils/statuses.js index 9aa785f63997e224beb0e8ab4492bdfbc6833410..848c521c4788f787de3e1c763de21cc659dfe31b 100644 --- a/bigbluebutton-html5/imports/utils/statuses.js +++ b/bigbluebutton-html5/imports/utils/statuses.js @@ -9,7 +9,6 @@ export const EMOJI_STATUSES = { applause: 'applause', thumbsUp: 'thumbs_up', thumbsDown: 'thumbs_down', - none: 'clear_status', }; export default { EMOJI_STATUSES };