diff --git a/web/chat/message.react.js b/web/chat/message.react.js --- a/web/chat/message.react.js +++ b/web/chat/message.react.js @@ -47,14 +47,7 @@ message = ; } else { invariant(item.robotext, "Flow can't handle our fancy types :("); - message = ( - - ); + message = ; } return (
diff --git a/web/chat/robotext-message.react.js b/web/chat/robotext-message.react.js --- a/web/chat/robotext-message.react.js +++ b/web/chat/robotext-message.react.js @@ -1,15 +1,15 @@ // @flow -import classNames from 'classnames'; +// import classNames from 'classnames'; import * as React from 'react'; import { useDispatch } from 'react-redux'; import { type RobotextChatMessageInfoItem } from 'lib/selectors/chat-selectors'; import { threadInfoSelector } from 'lib/selectors/thread-selectors'; import { splitRobotext, parseRobotextEntity } from 'lib/shared/message-utils'; -import { useSidebarExistsOrCanBeCreated } from 'lib/shared/thread-utils'; import type { Dispatch } from 'lib/types/redux-types'; import { type ThreadInfo } from 'lib/types/thread-types'; +import { longAbsoluteDate } from 'lib/utils/date-utils'; import Markdown from '../markdown/markdown.react'; import { linkRules } from '../markdown/rules.react'; @@ -17,37 +17,42 @@ import { updateNavInfoActionType } from '../types/nav-types'; import { InlineSidebar } from './inline-sidebar.react'; import MessageTooltip from './message-tooltip.react'; -import type { - MessagePositionInfo, - OnMessagePositionWithContainerInfo, -} from './position-types'; import css from './robotext-message.css'; -import { tooltipPositions } from './tooltip-utils'; +import { useTooltipContext } from './tooltip-provider'; +import { + calculateTooltipSize, + findTooltipPosition, + getMessageActionTooltipStyle, + tooltipPositions, + useRobotextMessageTooltipActions, +} from './tooltip-utils'; -// eslint-disable-next-line no-unused-vars const availableTooltipPositionsForRobotext = [ - tooltipPositions.RIGHT_TOP, - tooltipPositions.RIGHT, tooltipPositions.LEFT, + tooltipPositions.LEFT_TOP, + tooltipPositions.LEFT_BOTTOM, + tooltipPositions.RIGHT, + tooltipPositions.RIGHT_TOP, + tooltipPositions.RIGHT_BOTTOM, ]; type BaseProps = { +item: RobotextChatMessageInfoItem, +threadInfo: ThreadInfo, - +setMouseOverMessagePosition: ( - messagePositionInfo: MessagePositionInfo, - ) => void, - +mouseOverMessagePosition: ?OnMessagePositionWithContainerInfo, }; type Props = { ...BaseProps, - // Redux state - +sidebarExistsOrCanBeCreated: boolean, + +onMouseLeave: ?() => mixed, + +onMouseEnter: (event: SyntheticEvent) => mixed, + containsInlineSidebar: boolean, }; class RobotextMessage extends React.PureComponent { render() { let inlineSidebar; - if (this.props.item.threadCreatedFromMessage) { + if ( + this.props.containsInlineSidebar && + this.props.item.threadCreatedFromMessage + ) { inlineSidebar = (
; - } - - let messageTooltipLinks; - if (messageTooltip) { - messageTooltipLinks = ( -
- {messageTooltip} -
- ); - } - return (
{this.linkedRobotext()} - {messageTooltipLinks}
{inlineSidebar}
@@ -131,25 +110,7 @@ return textParts; } - - onMouseEnter = (event: SyntheticEvent) => { - const { item } = this.props; - const rect = event.currentTarget.getBoundingClientRect(); - const { top, bottom, left, right, height, width } = rect; - const messagePosition = { top, bottom, left, right, height, width }; - this.props.setMouseOverMessagePosition({ - type: 'on', - item, - messagePosition, - }); - }; - - onMouseLeave = () => { - const { item } = this.props; - this.props.setMouseOverMessagePosition({ type: 'off', item }); - }; } - type BaseInnerThreadEntityProps = { +id: string, +name: string, @@ -198,14 +159,86 @@ const ConnectedRobotextMessage: React.ComponentType = React.memo( function ConnectedRobotextMessage(props) { - const sidebarExistsOrCanBeCreated = useSidebarExistsOrCanBeCreated( - props.threadInfo, - props.item, + const { item, threadInfo } = props; + const [ + onMouseLeaveCallback, + setOnMouseLeaveCallback, + ] = React.useState mixed>(null); + + const { renderTooltip } = useTooltipContext(); + + const containsInlineSidebar = !!props.item.threadCreatedFromMessage; + + const timeZone = useSelector(state => state.timeZone); + + const messageTimestamp = React.useMemo(() => { + const time = props.item.messageInfo.time; + return longAbsoluteDate(time, timeZone); + }, [props.item.messageInfo.time, timeZone]); + + const tooltipActions = useRobotextMessageTooltipActions(item, threadInfo); + + const tooltip = React.useMemo( + () => ( + + ), + [messageTimestamp, tooltipActions], + ); + + const onMouseEnter = React.useCallback( + (event: SyntheticEvent) => { + const rect = event.currentTarget.getBoundingClientRect(); + const { top, bottom, left, right, height, width } = rect; + const messagePosition = { top, bottom, left, right, height, width }; + + if (renderTooltip) { + const tooltipLabels = tooltipActions.map(action => action.label); + const tooltipSize = calculateTooltipSize({ + tooltipLabels, + timestamp: messageTimestamp, + }); + const tooltipPosition = findTooltipPosition({ + sourcePositionInfo: messagePosition, + tooltipSize, + availablePositions: availableTooltipPositionsForRobotext, + preventDisplayingBelowSource: containsInlineSidebar, + }); + if (!tooltipPosition) { + return; + } + const tooltipPositionStyle = getMessageActionTooltipStyle({ + tooltipPosition, + sourcePositionInfo: messagePosition, + tooltipSize: tooltipSize, + }); + + const renderTooltipResult = renderTooltip({ + newNode: tooltip, + tooltipPosition: tooltipPositionStyle, + }); + if (renderTooltipResult) { + const { onMouseLeaveCallback: callback } = renderTooltipResult; + setOnMouseLeaveCallback((() => callback: () => () => mixed)); + } + } + }, + [ + containsInlineSidebar, + messageTimestamp, + renderTooltip, + tooltip, + tooltipActions, + ], ); return ( ); },