Merge pull request #18168 from ramonlsouza/issue-18134

refactor: Restyle Reactions bar to be a bar
This commit is contained in:
Ramón Souza 2023-06-28 17:27:45 -03:00 committed by GitHub
commit be48f7349f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 277 additions and 203 deletions

View File

@ -23,6 +23,7 @@ class ActionsBar extends PureComponent {
this.setCaptionsReaderMenuModalIsOpen = this.setCaptionsReaderMenuModalIsOpen.bind(this);
this.setRenderRaiseHand = this.renderRaiseHand.bind(this);
this.actionsBarRef = React.createRef();
}
setCaptionsReaderMenuModalIsOpen(value) {
@ -35,8 +36,12 @@ class ActionsBar extends PureComponent {
} = this.props;
return (<>
{isInteractionsButtonEnabled ? <InteractionsButtonContainer />
: isRaiseHandButtonEnabled ? <RaiseHandDropdownContainer {...{ setEmojiStatus, currentUser, intl }} />
{isInteractionsButtonEnabled ?
<>
<Styled.Separator />
<InteractionsButtonContainer actionsBarRef={this.actionsBarRef} />
</> :
isRaiseHandButtonEnabled ? <RaiseHandDropdownContainer {...{ setEmojiStatus, currentUser, intl }} />
: null}
</>);
}
@ -78,6 +83,7 @@ class ActionsBar extends PureComponent {
|| isSharingVideo || hasScreenshare || isSharedNotesPinned;
return (
<Styled.ActionsBar
ref={this.actionsBarRef}
style={
{
height: actionsBarStyle.innerHeight,

View File

@ -1,10 +1,8 @@
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import { defineMessages } from 'react-intl';
import PropTypes from 'prop-types';
import BBBMenu from '/imports/ui/components/common/menu/component';
import Button from '/imports/ui/components/common/button/component';
import ButtonEmoji from '/imports/ui/components/common/button/button-emoji/ButtonEmoji';
import ReactionsPicker from '/imports/ui/components/emoji-picker/reactions-picker/component';
import ReactionsBar from '/imports/ui/components/emoji-picker/reactions-bar/component';
import UserReactionService from '/imports/ui/components/user-reaction/service';
import UserListService from '/imports/ui/components/user-list/service';
@ -12,188 +10,46 @@ import Styled from '../styles';
const InteractionsButton = (props) => {
const {
userId,
emoji,
away,
raiseHand,
intl,
actionsBarRef,
userId,
raiseHand,
isMobile,
isRTL,
} = props;
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
const [dropdownList, setDropdownList] = useState([]);
const intlMessages = defineMessages({
raiseHandLabel: {
id: 'app.actionsBar.interactions.raiseHand',
description: 'raise Hand Label',
},
notRaiseHandLabel: {
id: 'app.actionsBar.interactions.lowHand',
description: 'not Raise Hand Label',
},
writeQuestionLabel: {
id: 'app.actionsBar.interactions.writeQuestion',
description: 'writeQuestion Label',
},
addReactionLabel: {
id: 'app.actionsBar.interactions.addReaction',
description: 'addReaction Label',
},
interactionsLabel: {
id: 'app.actionsBar.interactions.interactions',
description: 'interactions Label',
},
interactionsAdvancedButton: {
id: 'app.actionsBar.interactions.interactionsAdvancedButton',
description: 'interactions Label',
},
presentLabel: {
id: 'app.actionsBar.interactions.present',
description: 'present Label',
},
awayLabel: {
id: 'app.actionsBar.interactions.away',
description: 'away Label',
},
statusLabel: {
id: 'app.actionsBar.interactions.status',
description: 'status Label',
},
backLabel: {
id: 'app.actionsBar.interactions.back',
description: 'back Label',
},
});
const handleSetDropdownList = () => {
const dropdownActions = [];
dropdownActions.push({
key: 'interactions-label',
dataTest: 'interactionsLabel',
label: intl.formatMessage(intlMessages.interactionsLabel),
disabled: true,
onClick: () => {},
});
dropdownActions.push({
key: 'raise-hand',
dataTest: 'raise-hand',
icon: 'hand',
label: raiseHand
? intl.formatMessage(intlMessages.notRaiseHandLabel)
: intl.formatMessage(intlMessages.raiseHandLabel),
onClick: () => {
UserListService.setUserRaiseHand(userId, !raiseHand);
},
});
if (UserReactionService.isEnabled()) {
dropdownActions.push({
key: 'setstatus',
dataTest: 'setstatus',
icon: 'happy',
label: intl.formatMessage(intlMessages.addReactionLabel),
onClick: () => {
setDropdownList([
{
key: 'back',
dataTest: 'back',
icon: 'left_arrow',
label: intl.formatMessage(intlMessages.backLabel),
onClick: () => {
setShowEmojiPicker(false);
setDropdownList(dropdownActions);
},
},
]);
setShowEmojiPicker(true);
},
});
}
dropdownActions.push({
key: 'StatusLabel',
dataTest: 'StatusLabel',
label: intl.formatMessage(intlMessages.statusLabel),
disabled: true,
dividerTop: true,
onClick: () => {},
});
return dropdownActions;
};
useEffect(() => {
setDropdownList(handleSetDropdownList);
}, [emoji]);
const handleClose = () => {
setShowEmojiPicker(false);
setDropdownList(handleSetDropdownList);
setTimeout(() => {
document.activeElement.blur();
}, 0);
};
const handleReactionSelect = (emojiObject) => {
UserReactionService.setUserReaction(emojiObject.native);
const handleReactionSelect = (reaction) => {
UserReactionService.setUserReaction(reaction);
handleClose();
};
const renderEmojiPicker = () => (
const handleRaiseHandButtonClick = () => {
UserListService.setUserRaiseHand(userId, !raiseHand);
handleClose();
};
const renderReactionsBar = () => (
<Styled.Wrapper>
<ReactionsPicker {...props} onEmojiSelect={handleReactionSelect} />
<ReactionsBar {...props} onReactionSelect={handleReactionSelect} onRaiseHand={handleRaiseHandButtonClick} />
</Styled.Wrapper>
);
const handlePresent = () => {
UserListService.setUserAway(userId, false);
};
const handleAFK = () => {
UserListService.setUserAway(userId, true);
};
const buttonStatus = () => (
<Styled.ButtonContainer>
<Button
label={intl.formatMessage(intlMessages.presentLabel)}
onClick={() => handlePresent()}
id="btn"
icon="user"
disabled={!away}
size="md"
color={!away ? 'primary' : 'default'}
/>
<Button
label={intl.formatMessage(intlMessages.awayLabel)}
onClick={() => handleAFK()}
id="btn"
icon="clear_status"
disabled={away}
size="md"
color={away ? 'primary' : 'default'}
/>
</Styled.ButtonContainer>
);
const handleButtonLabel = () => {
if (isMobile) {
return intl.formatMessage(intlMessages.interactionsAdvancedButton);
}
return raiseHand
? intl.formatMessage(intlMessages.notRaiseHandLabel)
: intl.formatMessage(intlMessages.raiseHandLabel);
};
const handleInteractionsButtonClick = (event) => {
if (isMobile) {
return;
}
event.stopPropagation();
UserListService.setUserRaiseHand(userId, !raiseHand);
};
const customStyles = { top: '-1rem', borderRadius: '1.7rem' };
return (
<BBBMenu
@ -202,39 +58,33 @@ const InteractionsButton = (props) => {
<Styled.RaiseHandButton
data-test="InteractionsButton"
icon="hand"
label={handleButtonLabel()}
label={intl.formatMessage(intlMessages.interactionsLabel)}
description="Interactions"
ghost={!raiseHand}
ghost={!showEmojiPicker}
onKeyPress={() => {}}
onClick={handleInteractionsButtonClick}
color={raiseHand ? 'primary' : 'default'}
onClick={() => setShowEmojiPicker(true)}
color={showEmojiPicker ? 'primary' : 'default'}
hideLabel
circle
size="lg"
/>
{!isMobile && (
<ButtonEmoji
data-test="interactions-advanced-button"
emoji="device_list_selector"
label={intl.formatMessage(intlMessages.interactionsAdvancedButton)}
hideLabel
tabIndex={0}
rotate
/>
)}
</Styled.InteractionsDropdown>
)}
actions={dropdownList}
renderOtherComponents={showEmojiPicker ? renderEmojiPicker() : buttonStatus()}
renderOtherComponents={showEmojiPicker ? renderReactionsBar() : null}
onCloseCallback={() => handleClose()}
customAnchorEl={!isMobile ? actionsBarRef.current : null}
customStyles={customStyles}
open={showEmojiPicker}
hasRoundedCorners
overrideMobileStyles
opts={{
id: 'default-dropdown-menu',
id: 'reactions-dropdown-menu',
keepMounted: true,
transitionDuration: 0,
elevation: 3,
getContentAnchorEl: null,
anchorOrigin: { vertical: 'top', horizontal: isRTL ? 'left' : 'right' },
transformOrigin: { vertical: 'bottom', horizontal: isRTL ? 'right' : 'left' },
anchorOrigin: { vertical: 'top', horizontal: 'center' },
transformOrigin: { vertical: 'bottom', horizontal: 'center' },
}}
/>
);
@ -248,7 +98,6 @@ const propTypes = {
emoji: PropTypes.string.isRequired,
sidebarContentPanel: PropTypes.string.isRequired,
layoutContextDispatch: PropTypes.func.isRequired,
isMobile: PropTypes.bool.isRequired,
};
InteractionsButton.propTypes = propTypes;

View File

@ -1,21 +1,22 @@
import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import { layoutSelectInput, layoutDispatch } from '/imports/ui/components/layout/context';
import deviceInfo from '/imports/utils/deviceInfo';
import { injectIntl } from 'react-intl';
import InteractionsButton from './component';
import actionsBarService from '../service';
import { layoutSelect } from '../../layout/context';
import { SMALL_VIEWPORT_BREAKPOINT } from '/imports/ui/components/layout/enums';
const InteractionsButtonContainer = ({ ...props }) => {
const layoutContextDispatch = layoutDispatch();
const sidebarContent = layoutSelectInput((i) => i.sidebarContent);
const { sidebarContentPanel } = sidebarContent;
const { isMobile } = deviceInfo;
const isRTL = layoutSelect((i) => i.isRTL);
const { width: browserWidth } = layoutSelectInput((i) => i.browser);
const isMobile = browserWidth <= SMALL_VIEWPORT_BREAKPOINT;
return (
<InteractionsButton {...{
layoutContextDispatch, sidebarContentPanel, isMobile, isRTL, ...props,
layoutContextDispatch, sidebarContentPanel, isMobile, ...props,
}}
/>
);
@ -27,7 +28,6 @@ export default injectIntl(withTracker(() => {
return {
userId: currentUser.userId,
emoji: currentUser.emoji,
away: currentUser.away,
raiseHand: currentUser.raiseHand,
};
})(InteractionsButtonContainer));

View File

@ -126,6 +126,14 @@ const Wrapper = styled.div`
}
`;
const Separator = styled.div`
height: 2.5rem;
width: 0;
border: 1px solid ${colorWhite};
align-self: center;
opacity: .75;
`;
export default {
ActionsBar,
Left,
@ -135,4 +143,5 @@ export default {
ButtonContainer,
InteractionsDropdown,
Wrapper,
Separator,
};

View File

@ -148,9 +148,22 @@ class BBBMenu extends React.Component {
render() {
const { anchorEl } = this.state;
const { trigger, intl, customStyles, dataTest, opts, accessKey, open, renderOtherComponents } = this.props;
const {
trigger,
intl,
customStyles,
dataTest,
opts,
accessKey,
open,
renderOtherComponents,
customAnchorEl,
hasRoundedCorners,
overrideMobileStyles,
} = this.props;
const actionsItems = this.makeMenuItems();
const roundedCornersStyles = { borderRadius: '1.8rem' };
let menuStyles = { zIndex: 999 };
if (customStyles) {
@ -184,16 +197,20 @@ class BBBMenu extends React.Component {
<Menu
{...opts}
{...this.optsToMerge}
anchorEl={anchorEl}
anchorEl={customAnchorEl ? customAnchorEl : anchorEl}
open={Boolean(anchorEl)}
onClose={this.handleClose}
style={menuStyles}
data-test={dataTest}
onKeyDownCapture={this.handleKeyDown}
PaperProps={{
style: hasRoundedCorners ? roundedCornersStyles : {},
className: overrideMobileStyles ? 'override-mobile-styles' : 'MuiPaper-root-mobile',
}}
>
{actionsItems}
{renderOtherComponents}
{anchorEl && window.innerWidth < SMALL_VIEWPORT_BREAKPOINT &&
{!overrideMobileStyles && anchorEl && window.innerWidth < SMALL_VIEWPORT_BREAKPOINT &&
<Styled.CloseButton
label={intl.formatMessage(intlMessages.close)}
size="lg"

View File

@ -0,0 +1,88 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, defineMessages } from 'react-intl';
import { Emoji } from 'emoji-mart';
import 'emoji-mart/css/emoji-mart.css';
import Styled from './styles';
const propTypes = {
intl: PropTypes.shape({
formatMessage: PropTypes.func.isRequired,
}).isRequired,
onEmojiSelect: PropTypes.func.isRequired,
};
const intlMessages = defineMessages({
raiseHandLabel: {
id: 'app.actionsBar.interactions.raiseHand',
description: 'raise Hand Label',
},
notRaiseHandLabel: {
id: 'app.actionsBar.interactions.lowHand',
description: 'not Raise Hand Label',
},
});
const reactions = [
{
id: 'smiley',
native: '😃',
},
{
id: 'neutral_face',
native: '😐',
},
{
id: 'slightly_frowning_face',
native: '🙁',
},
{
id: '+1',
native: '👍',
},
{
id: '-1',
native: '👎',
},
{
id: 'clap',
native: '👏',
},
];
const ReactionsPicker = (props) => {
const {
intl,
onReactionSelect,
onRaiseHand,
raiseHand,
isMobile,
} = props;
const RaiseHandButtonLabel = () => {
if (isMobile) return null;
return raiseHand
? intl.formatMessage(intlMessages.notRaiseHandLabel)
: intl.formatMessage(intlMessages.raiseHandLabel);
};
return (
<Styled.Wrapper isMobile={isMobile}>
{reactions.map(({ id, native }) => (
<Styled.ButtonWrapper>
<Emoji key={id} emoji={{ id }} size={30} onClick={() => onReactionSelect(native)} />
</Styled.ButtonWrapper>
))}
<Styled.Separator isMobile={isMobile} />
<Styled.RaiseHandButtonWrapper onClick={() => onRaiseHand()} active={raiseHand}>
<Emoji key='hand' emoji={{ id: 'hand' }} size={30} />
{RaiseHandButtonLabel()}
</Styled.RaiseHandButtonWrapper>
</Styled.Wrapper>
);
};
ReactionsPicker.propTypes = propTypes;
export default injectIntl(ReactionsPicker);

View File

@ -0,0 +1,81 @@
import styled from 'styled-components';
import {
colorOffWhite,
colorGrayLightest,
btnPrimaryHoverBg,
btnPrimaryColor,
btnPrimaryBg,
} from '/imports/ui/stylesheets/styled-components/palette';
const Wrapper = styled.div`
display: flex;
padding: .5rem;
${({ isMobile }) => isMobile && `
flex-direction: column;
align-items: center;
padding: .5rem 0;
`}
`;
const ButtonWrapper = styled.div`
border: none;
cursor: pointer;
height: 2.5rem;
width: 2.5rem;
display: flex;
align-items: center;
border-radius: 50%;
margin: 0 .5rem;
&:hover {
background-color: ${colorOffWhite};
}
& > button {
cursor: pointer;
flex: auto;
}
& > * > span {
height: 1.8rem !important;
width: 1.8rem !important;
}
`;
const RaiseHandButtonWrapper = styled(ButtonWrapper)`
width: auto;
border: 1px solid ${colorGrayLightest};
border-radius: 1.7rem;
padding: 1rem 0.5rem;
${({ active }) => active && `
color: ${btnPrimaryColor};
background-color: ${btnPrimaryBg};
&:hover{
filter: brightness(90%);
color: ${btnPrimaryColor};
background-color: ${btnPrimaryHoverBg} !important;
}
`}
`
const Separator = styled.div`
height: 2.5rem;
width: 0;
border: 1px solid ${colorGrayLightest};
align-self: center;
opacity: .75;
${({ isMobile }) => isMobile && `
display: none;
`}
`;
export default {
Wrapper,
ButtonWrapper,
RaiseHandButtonWrapper,
Separator,
};

View File

@ -484,6 +484,8 @@ const getAvailableActions = (
&& !amISubjectUser
&& usersProp.allowModsToEjectCameras;
const allowedToSetAway = amISubjectUser && !USER_STATUS_ENABLED;
return {
allowedToChatPrivately,
allowedToMuteAudio,
@ -497,6 +499,7 @@ const getAvailableActions = (
allowedToChangeUserLockStatus,
allowedToChangeWhiteboardAccess,
allowedToEjectCameras,
allowedToSetAway,
};
};

View File

@ -124,6 +124,7 @@ class UserParticipants extends Component {
const {
compact,
setEmojiStatus,
setUserAway,
users,
requestUserInformation,
currentUser,
@ -152,6 +153,7 @@ class UserParticipants extends Component {
{...{
compact,
setEmojiStatus,
setUserAway,
requestUserInformation,
currentUser,
meetingIsBreakout,

View File

@ -15,6 +15,7 @@ const UserParticipantsContainer = (props) => {
const {
formatUsers,
setEmojiStatus,
setUserAway,
clearAllEmojiStatus,
roving,
requestUserInformation,
@ -33,6 +34,7 @@ const UserParticipantsContainer = (props) => {
currentUser,
users,
setEmojiStatus,
setUserAway,
clearAllEmojiStatus,
roving,
requestUserInformation,

View File

@ -151,6 +151,14 @@ const messages = defineMessages({
id: 'app.createBreakoutRoom.room',
description: 'breakout room',
},
awayLabel: {
id: 'app.userList.menu.away',
description: 'Text for identifying away user',
},
notAwayLabel: {
id: 'app.userList.menu.notAway',
description: 'Text for identifying not away user',
},
});
const propTypes = {
@ -280,6 +288,7 @@ class UserListItem extends PureComponent {
getGroupChatPrivate,
getEmojiList,
setEmojiStatus,
setUserAway,
assignPresenter,
removeUser,
toggleVoice,
@ -322,6 +331,7 @@ class UserListItem extends PureComponent {
allowedToChangeUserLockStatus,
allowedToChangeWhiteboardAccess,
allowedToEjectCameras,
allowedToSetAway,
} = actionPermissions;
const { disablePrivateChat } = lockSettingsProps;
@ -538,6 +548,17 @@ class UserListItem extends PureComponent {
},
icon: 'video_off',
},
{
allowed: allowedToSetAway
&& isMeteorConnected,
key: 'setAway',
label: intl.formatMessage(user.away ? messages.notAwayLabel : messages.awayLabel),
onClick: () => {
this.onActionsHide(setUserAway(user.userId, !user.away));
this.handleClose();
},
icon: 'time',
},
];
const statuses = Object.keys(getEmojiList);

View File

@ -20,12 +20,12 @@ const GlobalStyle = createGlobalStyle`
.MuiPopover-root {
top: 0 !important;
}
.MuiPaper-root.MuiMenu-paper.MuiPopover-paper {
.MuiPaper-root-mobile {
top: 0 !important;
left: 0 !important;
bottom: 0 !important;
right: 0 !important;
max-width: none;
max-width: none !important;
}
}
.MuiList-padding {

View File

@ -137,6 +137,8 @@
"app.userList.menuTitleContext": "Available options",
"app.userList.chatListItem.unreadSingular": "One new message",
"app.userList.chatListItem.unreadPlural": "{0} new messages",
"app.userList.menu.away": "Set yourself as away",
"app.userList.menu.notAway": "Set yourself as active",
"app.userList.menu.chat.label": "Start a private chat",
"app.userList.menu.clearStatus.label": "Clear status",
"app.userList.menu.removeUser.label": "Remove user",
@ -631,15 +633,9 @@
"app.actionsBar.actionsDropdown.selectRandUserDesc": "Chooses a user from available viewers at random",
"app.actionsBar.actionsDropdown.propagateLayoutLabel": "Propagate layout",
"app.actionsBar.interactions.interactions": "Interactions",
"app.actionsBar.interactions.interactionsAdvancedButton": "Open interactions submenu",
"app.actionsBar.interactions.raiseHand": "Raise your hand",
"app.actionsBar.interactions.lowHand": "Lower your hand",
"app.actionsBar.interactions.writeQuestion": "Write a question",
"app.actionsBar.interactions.addReaction": "Add a reaction",
"app.actionsBar.interactions.status": "Status",
"app.actionsBar.interactions.present": "Present",
"app.actionsBar.interactions.away": "Away",
"app.actionsBar.interactions.back": "Back",
"app.actionsBar.emojiMenu.statusTriggerLabel": "Set status",
"app.actionsBar.emojiMenu.awayLabel": "Away",
"app.actionsBar.emojiMenu.awayDesc": "Change your status to away",