diff --git a/web/chat/chat-message-list.react.js b/web/chat/chat-message-list.react.js --- a/web/chat/chat-message-list.react.js +++ b/web/chat/chat-message-list.react.js @@ -32,6 +32,7 @@ import { useSelector } from '../redux/redux-utils'; import css from './chat-message-list.css'; import { MessageListContext } from './message-list-types'; +import MessageTimestampTooltip from './message-timestamp-tooltip.react'; import Message from './message.react'; import type { OnMessagePositionWithContainerInfo, @@ -237,6 +238,17 @@ invariant(inputState, 'InputState should be set'); const messages = messageListData.map(this.renderItem); + let tooltip; + if (this.state.mouseOverMessagePosition) { + const messagePositionInfo = this.state.mouseOverMessagePosition; + tooltip = ( + + ); + } + let relationshipPrompt; if (threadInfo) { relationshipPrompt = ; @@ -252,6 +264,7 @@
{messages}
+ {tooltip} ); } diff --git a/web/chat/message-timestamp-tooltip.css b/web/chat/message-timestamp-tooltip.css new file mode 100644 --- /dev/null +++ b/web/chat/message-timestamp-tooltip.css @@ -0,0 +1,53 @@ +div.messageLeftTooltip:after { + top: 7px; + right: -14px; + border-color: transparent transparent transparent var(--tool-tip-bg); +} +div.messageRightTooltip:after { + top: 7px; + left: -14px; + border-color: transparent var(--tool-tip-bg) transparent transparent; +} +div.messageTopLeftTooltip:after { + bottom: -14px; + left: 4px; + border-color: var(--tool-tip-bg) transparent transparent transparent; +} +div.messageTopRightTooltip:after { + bottom: -14px; + right: 4px; + border-color: var(--tool-tip-bg) transparent transparent transparent; +} +div.messageBottomLeftTooltip:after { + top: -14px; + left: 4px; + border-color: transparent transparent var(--tool-tip-bg) transparent; +} +div.messageBottomRightTooltip:after { + top: -14px; + right: 4px; + border-color: transparent transparent var(--tool-tip-bg) transparent; +} + +div.messageActionActiveArea { + position: absolute; + display: flex; + top: 0; + bottom: 0; + align-items: center; + padding: 0 12px; +} + +div.viewerMessageActionActiveArea { + right: 100%; +} +div.nonViewerMessageActiveArea { + left: 100%; +} +div.messageActionActiveArea > div + div { + margin-left: 4px; +} + +div.messageActionLinkIcon:hover { + cursor: pointer; +} diff --git a/web/chat/message-timestamp-tooltip.react.js b/web/chat/message-timestamp-tooltip.react.js new file mode 100644 --- /dev/null +++ b/web/chat/message-timestamp-tooltip.react.js @@ -0,0 +1,148 @@ +// @flow + +import invariant from 'invariant'; +import * as React from 'react'; + +import { isComposableMessageType } from 'lib/types/message-types'; +import { longAbsoluteDate } from 'lib/utils/date-utils'; + +import css from './message-timestamp-tooltip.css'; +import type { OnMessagePositionWithContainerInfo } from './position-types'; +import { + type TooltipPosition, + tooltipPositions, + sizeOfTooltipArrow, +} from './tooltip-utils'; +import { + TooltipMenu, + type TooltipStyle, + TooltipTextItem, +} from './tooltip.react'; + +const availablePositionsForComposedViewerMessage = [ + tooltipPositions.BOTTOM_RIGHT, +]; +const availablePositionsForNonComposedOrNonViewerMessage = [ + tooltipPositions.LEFT, +]; + +type Props = { + +messagePositionInfo: OnMessagePositionWithContainerInfo, + +timeZone: ?string, +}; +function MessageTimestampTooltip(props: Props): React.Node { + const { messagePositionInfo, timeZone } = props; + const { time, creator, type } = messagePositionInfo.item.messageInfo; + console.log(messagePositionInfo); + + const text = React.useMemo(() => longAbsoluteDate(time, timeZone), [ + time, + timeZone, + ]); + const availableTooltipPositions = React.useMemo(() => { + const { isViewer } = creator; + const isComposed = isComposableMessageType(type); + return isComposed && isViewer + ? availablePositionsForComposedViewerMessage + : availablePositionsForNonComposedOrNonViewerMessage; + }, [creator, type]); + + const { messagePosition, containerPosition } = messagePositionInfo; + const pointingToInfo = React.useMemo(() => { + return { + containerPosition, + itemPosition: messagePosition, + }; + }, [messagePosition, containerPosition]); + + const getTooltipStyle = React.useCallback( + (tooltipPosition: TooltipPosition) => + getTimestampTooltipStyle(messagePositionInfo, tooltipPosition), + [messagePositionInfo], + ); + return ( + + + + ); +} + +function getTimestampTooltipStyle( + messagePositionInfo: OnMessagePositionWithContainerInfo, + tooltipPosition: TooltipPosition, +): TooltipStyle { + const { messagePosition, containerPosition } = messagePositionInfo; + const { height: containerHeight, width: containerWidth } = containerPosition; + + let style, className; + if (tooltipPosition === tooltipPositions.LEFT) { + const centerOfMessage = messagePosition.top + messagePosition.height / 2; + const tooltipPointing = Math.max( + Math.min(centerOfMessage, containerHeight), + 0, + ); + style = { + right: containerWidth - messagePosition.left + sizeOfTooltipArrow, + top: tooltipPointing, + }; + className = css.messageLeftTooltip; + } else if (tooltipPosition === tooltipPositions.RIGHT) { + const centerOfMessage = messagePosition.top + messagePosition.height / 2; + const tooltipPointing = Math.max( + Math.min(centerOfMessage, containerHeight), + 0, + ); + style = { + left: messagePosition.right + sizeOfTooltipArrow, + top: tooltipPointing, + }; + className = css.messageRightTooltip; + } else if (tooltipPosition === tooltipPositions.TOP_LEFT) { + const tooltipPointing = Math.min( + containerHeight - messagePosition.top, + containerHeight, + ); + style = { + left: messagePosition.left, + bottom: tooltipPointing + sizeOfTooltipArrow, + }; + className = css.messageTopLeftTooltip; + } else if (tooltipPosition === tooltipPositions.TOP_RIGHT) { + const tooltipPointing = Math.min( + containerHeight - messagePosition.top, + containerHeight, + ); + style = { + right: containerWidth - messagePosition.right, + bottom: tooltipPointing + sizeOfTooltipArrow, + }; + className = css.messageTopRightTooltip; + } else if (tooltipPosition === tooltipPositions.BOTTOM_LEFT) { + const tooltipPointing = Math.min(messagePosition.bottom, containerHeight); + style = { + left: messagePosition.left, + top: tooltipPointing + sizeOfTooltipArrow, + }; + className = css.messageBottomLeftTooltip; + } else if (tooltipPosition === tooltipPositions.BOTTOM_RIGHT) { + const tooltipPointing = Math.min(messagePosition.bottom, containerHeight); + style = { + right: containerWidth - messagePosition.right, + top: tooltipPointing + sizeOfTooltipArrow, + }; + className = css.messageBottomRightTooltip; + console.log(`container height:${containerHeight}`); + } + invariant( + className && style, + `${tooltipPosition} is not valid for timestamp tooltip`, + ); + return { className, style }; +} + +export default MessageTimestampTooltip; diff --git a/web/chat/message-tooltip.css b/web/chat/message-tooltip.css --- a/web/chat/message-tooltip.css +++ b/web/chat/message-tooltip.css @@ -9,24 +9,6 @@ width: fit-content; } -div.timestampContainer { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - padding: 0 6px; - margin-top: 6px; - background-color: var(--message-action-tooltip-bg); - border-radius: 8px; - color: var(--tool-tip-color); -} - -div.timestampContainer p { - white-space: nowrap; - padding: 5px; - font-size: var(--s-font-14); -} - div.messageActionButtons { display: flex; font-size: 16px; diff --git a/web/chat/message-tooltip.react.js b/web/chat/message-tooltip.react.js --- a/web/chat/message-tooltip.react.js +++ b/web/chat/message-tooltip.react.js @@ -7,10 +7,8 @@ import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors'; import { useSidebarExistsOrCanBeCreated } from 'lib/shared/thread-utils'; import type { ThreadInfo } from 'lib/types/thread-types'; -import { longAbsoluteDate } from 'lib/utils/date-utils'; import type { InputState } from '../input/input-state'; -import { useSelector } from '../redux/redux-utils'; import { useOnClickThread, useOnClickPendingSidebar, @@ -190,12 +188,6 @@ ); } - const timezone = useSelector(state => state.timeZone); - const timestampText = React.useMemo( - () => longAbsoluteDate(messageInfo.time, timezone), - [messageInfo.time, timezone], - ); - const { isViewer } = messageInfo.creator; const messageActionButtonsContainerClassName = classNames({ [css.messageActionContainer]: true, @@ -209,9 +201,6 @@ {sidebarButton} {replyButton} -
-

{timestampText}

-
); }