import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage, FormattedRelative } from 'react-intl'; import classnames from 'classnames'; import { Flyout, Overlay } from '../../components/flyout'; import Tooltip from '../../components/tooltip'; // GROWTH-382 import Button from '../../components/button'; import { ARROW_DOWN, ENTER, SPACE } from '../../common/keyboard-events'; import PresenceDropdown from './PresenceDropdown'; import PresenceAvatar from './PresenceAvatar'; import { determineInteractionMessage } from './utils/presenceUtils'; import { collaboratorsPropType, flyoutPositionPropType } from './propTypes'; // GROWTH-382 import { GROWTH_382_EXPERIMENT_BUCKET, GROWTH_382_AUTOFLY_CLASS, GROWTH_382_AUTOFLY_CLASS_FIRST_LOAD, } from './constants'; import messages from './messages'; import './Presence.scss'; class Presence extends Component { /* eslint-disable no-underscore-dangle */ static propTypes = { /** Addtional attributes for avatar container */ avatarAttributes: PropTypes.object, className: PropTypes.string, collaborators: PropTypes.arrayOf(collaboratorsPropType).isRequired, /** Addtional attributes for presence container */ containerAttributes: PropTypes.object, /** Get Link callback */ getLinkCallback: PropTypes.func, /** Invite button callback */ inviteCallback: PropTypes.func, /** Maximum number of avatars to display before showing a +{n} avatar */ maxDisplayedAvatars: PropTypes.number, /** Maximum number of collaborators before displaying a {maxAdditionalCollaboratorsNum}+ avatar */ maxAdditionalCollaboratorsNum: PropTypes.number, /** Callback funtion for avatar mouseEnter, argument: id of user */ onAvatarMouseEnter: PropTypes.func, /** Callback function for avatar mouseLeave */ onAvatarMouseLeave: PropTypes.func, /** Callback funtion for Flyout events, argument: SyntheticEvent */ onFlyoutClose: PropTypes.func, onFlyoutOpen: PropTypes.func, onFlyoutScroll: PropTypes.func, /** GROWTH-382 bucketing */ experimentBucket: PropTypes.string, /** GROWTH-382 broadcast that the user wants to view stats from the flyout */ onAccessStatsRequested: PropTypes.func, /** GROWTH-382 log that the user wants to view collaborators from the flyout */ onClickViewCollaborators: PropTypes.func, /** Option to change the orientation of the dropdown. MUST be: bottom-right, bottom-left, bottom-center etc. or in this specific format */ flyoutPosition: flyoutPositionPropType, /** Sets the tether constraint to scrollParent for the flyout */ constrainToScrollParent: PropTypes.bool, /** Sets the tether constraint to window for the flyout */ constrainToWindow: PropTypes.bool, /** Closes the flyout when window loses focus */ closeOnWindowBlur: PropTypes.bool, }; static defaultProps = { className: '', maxDisplayedAvatars: 3, maxAdditionalCollaboratorsNum: 99, experimentBucket: null, flyoutPosition: 'bottom-left', constrainToScrollParent: true, constrainToWindow: false, closeOnWindowBlur: false, }; state = { activeTooltip: null, isDropdownActive: false, showActivityPrompt: Boolean( this.props.collaborators.length && this.props.onClickViewCollaborators && this.props.experimentBucket === GROWTH_382_EXPERIMENT_BUCKET, ), }; saveRefToContainer = el => { this.presenceContainerEl = el; }; _showTooltip = id => { const { onAvatarMouseEnter } = this.props; this.setState({ activeTooltip: id, }); if (onAvatarMouseEnter) { onAvatarMouseEnter(id); } }; _hideTooltip = () => { const { onAvatarMouseLeave } = this.props; this.setState({ activeTooltip: null, }); if (onAvatarMouseLeave) { onAvatarMouseLeave(); } }; _handleOverlayOpen = event => { const { onFlyoutOpen } = this.props; this.setState({ isDropdownActive: true, }); if (onFlyoutOpen) { onFlyoutOpen(event); } }; _handleOverlayClose = event => { const { onFlyoutClose } = this.props; this.setState({ isDropdownActive: false, }); if (onFlyoutClose) { onFlyoutClose(event); } }; stopPropagationAndPreventDefault = event => { event.stopPropagation(); event.preventDefault(); }; openDropDown = () => { if (this.presenceContainerEl) { this.presenceContainerEl.click(); } }; handleKeyDown = event => { switch (event.key) { case ARROW_DOWN: case ENTER: case SPACE: this.openDropDown(); this.stopPropagationAndPreventDefault(event); break; default: break; } }; _renderTimestampMessage = (interactedAt, interactionType, isActive) => { const lastActionMessage = determineInteractionMessage(interactionType); if (lastActionMessage) { return (