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 @@ -1,3 +1,13 @@ +div.container { + display: flex; + flex-direction: row; +} + +div.containerLeftAlign, +div.containerCenterAlign { + flex-direction: row-reverse; +} + div.messageTooltipContainer { display: flex; flex-direction: column; 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 @@ -1,8 +1,15 @@ // @flow +import data from '@emoji-mart/data'; +import Picker from '@emoji-mart/react'; import classNames from 'classnames'; import * as React from 'react'; +import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors'; +import { localIDPrefix } from 'lib/shared/message-utils'; +import type { ThreadInfo } from 'lib/types/thread-types'; + +import { useSelector } from '../redux/redux-utils'; import { type MessageTooltipAction } from '../utils/tooltip-utils'; import { tooltipButtonStyle, @@ -10,15 +17,30 @@ tooltipStyle, } from './chat-constants'; import css from './message-tooltip.css'; +import { useOnClickReact } from './reaction-message-utils'; +import { useTooltipContext } from './tooltip-provider'; type MessageTooltipProps = { +actions: $ReadOnlyArray, +messageTimestamp: string, +alignment?: 'left' | 'center' | 'right', + +item: ChatMessageInfoItem, + +threadInfo: ThreadInfo, }; function MessageTooltip(props: MessageTooltipProps): React.Node { - const { actions, messageTimestamp, alignment = 'left' } = props; + const { + actions, + messageTimestamp, + alignment = 'left', + item, + threadInfo, + } = props; + const { messageInfo, reactions } = item; + const [activeTooltipLabel, setActiveTooltipLabel] = React.useState(); + + const { renderEmojiKeyboard } = useTooltipContext(); + const messageActionButtonsContainerClassName = classNames( css.messageActionContainer, css.messageActionButtons, @@ -91,19 +113,62 @@ ); }, [messageTimestamp, messageTooltipLabelStyle]); + const nextLocalID = useSelector(state => state.nextLocalID); + const localID = `${localIDPrefix}${nextLocalID}`; + + const onClickReact = useOnClickReact(); + + const onEmojiSelect = React.useCallback( + emoji => { + const reactionInput = emoji.native; + const viewerReacted = !!reactions.get(reactionInput)?.viewerReacted; + const action = viewerReacted ? 'remove_reaction' : 'add_reaction'; + + onClickReact( + messageInfo.id, + localID, + threadInfo.id, + reactionInput, + action, + ); + }, + [localID, messageInfo.id, onClickReact, reactions, threadInfo.id], + ); + + const emojiKeyboard = React.useMemo(() => { + if (!renderEmojiKeyboard) { + return null; + } + return ; + }, [onEmojiSelect, renderEmojiKeyboard]); + const messageTooltipContainerStyle = React.useMemo(() => tooltipStyle, []); - const containerClassNames = classNames(css.messageTooltipContainer, { - [css.leftTooltipAlign]: alignment === 'left', - [css.centerTooltipAlign]: alignment === 'center', - [css.rightTooltipAlign]: alignment === 'right', + const containerClassName = classNames(css.container, { + [css.containerLeftAlign]: alignment === 'left', + [css.containerCenterAlign]: alignment === 'center', }); + const messageTooltipContainerClassNames = classNames( + css.messageTooltipContainer, + { + [css.leftTooltipAlign]: alignment === 'left', + [css.centerTooltipAlign]: alignment === 'center', + [css.rightTooltipAlign]: alignment === 'right', + }, + ); + return ( -
-
{tooltipLabel}
- {tooltipButtons} - {tooltipTimestamp} +
+ {emojiKeyboard} +
+
{tooltipLabel}
+ {tooltipButtons} + {tooltipTimestamp} +
); } diff --git a/web/utils/tooltip-utils.js b/web/utils/tooltip-utils.js --- a/web/utils/tooltip-utils.js +++ b/web/utils/tooltip-utils.js @@ -5,7 +5,7 @@ import * as React from 'react'; import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors'; -import { localIDPrefix, createMessageReply } from 'lib/shared/message-utils'; +import { createMessageReply } from 'lib/shared/message-utils'; import { useCanCreateReactionFromMessage } from 'lib/shared/reaction-utils'; import { threadHasPermission, @@ -21,18 +21,16 @@ tooltipLabelStyle, tooltipStyle, } from '../chat/chat-constants'; -import MessageLikeTooltipButton from '../chat/message-like-tooltip-button.react'; import MessageTooltip from '../chat/message-tooltip.react'; import type { PositionInfo } from '../chat/position-types'; -import { useOnClickReact } from '../chat/reaction-message-utils'; import { useTooltipContext } from '../chat/tooltip-provider'; import CommIcon from '../CommIcon.react'; import { InputStateContext } from '../input/input-state'; -import { useSelector } from '../redux/redux-utils'; import { useOnClickPendingSidebar, useOnClickThread, } from '../selectors/thread-selectors'; +import SWMansionIcon from '../SWMansionIcon.react'; import { calculateMaxTextWidth } from '../utils/text-utils'; export const tooltipPositions = Object.freeze({ @@ -457,28 +455,9 @@ item: ChatMessageInfoItem, threadInfo: ThreadInfo, ): ?MessageTooltipAction { - const { messageInfo, reactions } = item; - - const nextLocalID = useSelector(state => state.nextLocalID); - const localID = `${localIDPrefix}${nextLocalID}`; - - const reactionInput = '👍'; - const viewerReacted = !!reactions.get(reactionInput)?.viewerReacted; - const action = viewerReacted ? 'remove_reaction' : 'add_reaction'; - - const onClickReact = useOnClickReact(); - - const onClick = React.useCallback( - () => - onClickReact( - messageInfo.id, - localID, - threadInfo.id, - reactionInput, - action, - ), - [action, localID, messageInfo.id, onClickReact, threadInfo.id], - ); + const { messageInfo } = item; + + const { setRenderEmojiKeyboard } = useTooltipContext(); const canCreateReactionFromMessage = useCanCreateReactionFromMessage( threadInfo, @@ -490,16 +469,21 @@ return null; } - const buttonContent = ( - - ); + const buttonContent = ; + + const onClickReact = () => { + if (!setRenderEmojiKeyboard) { + return; + } + setRenderEmojiKeyboard(true); + }; return { actionButtonContent: buttonContent, - onClick, - label: viewerReacted ? 'Unlike' : 'Like', + onClick: onClickReact, + label: 'React', }; - }, [canCreateReactionFromMessage, onClick, viewerReacted]); + }, [canCreateReactionFromMessage, setRenderEmojiKeyboard]); } function useMessageTooltipActions( @@ -534,6 +518,8 @@ +containsInlineEngagement: boolean, +tooltipActions: $ReadOnlyArray, +messageTimestamp: string, + +item: ChatMessageInfoItem, + +threadInfo: ThreadInfo, }; function createTooltip(params: CreateTooltipParams) { @@ -544,6 +530,8 @@ containsInlineEngagement, tooltipActions, messageTimestamp, + item, + threadInfo, } = params; if (!tooltipMessagePosition) { return; @@ -572,6 +560,8 @@ actions={tooltipActions} messageTimestamp={messageTimestamp} alignment={alignment} + item={item} + threadInfo={threadInfo} /> ); return { tooltip, tooltipPositionStyle }; @@ -628,6 +618,8 @@ containsInlineEngagement, tooltipActions, messageTimestamp, + item, + threadInfo, }); if (!tooltipResult) { return; @@ -647,8 +639,10 @@ [ availablePositions, containsInlineEngagement, + item, messageTimestamp, renderTooltip, + threadInfo, tooltipActions, tooltipMessagePosition, tooltipSize, @@ -667,6 +661,8 @@ containsInlineEngagement, tooltipActions, messageTimestamp, + item, + threadInfo, }); if (!tooltipResult) { return; @@ -676,7 +672,9 @@ }, [ availablePositions, containsInlineEngagement, + item, messageTimestamp, + threadInfo, tooltipActions, tooltipMessagePosition, tooltipSize,