Changeset View
Changeset View
Standalone View
Standalone View
web/chat/message-tooltip.react.js
Show All 9 Lines | |||||
import type { ThreadInfo } from 'lib/types/thread-types.js'; | import type { ThreadInfo } from 'lib/types/thread-types.js'; | ||||
import { | import { | ||||
tooltipButtonStyle, | tooltipButtonStyle, | ||||
tooltipLabelStyle, | tooltipLabelStyle, | ||||
tooltipStyle, | tooltipStyle, | ||||
} from './chat-constants.js'; | } from './chat-constants.js'; | ||||
import css from './message-tooltip.css'; | import css from './message-tooltip.css'; | ||||
import { useSendReaction } from './reaction-message-utils.js'; | import { | ||||
useSendReaction, | |||||
getEmojiKeyboardPosition, | |||||
} from './reaction-message-utils.js'; | |||||
import { useTooltipContext } from './tooltip-provider.js'; | import { useTooltipContext } from './tooltip-provider.js'; | ||||
import { useSelector } from '../redux/redux-utils.js'; | import { useSelector } from '../redux/redux-utils.js'; | ||||
import { type MessageTooltipAction } from '../utils/tooltip-utils.js'; | import type { | ||||
MessageTooltipAction, | |||||
TooltipSize, | |||||
TooltipPositionStyle, | |||||
} from '../utils/tooltip-utils.js'; | |||||
type MessageTooltipProps = { | type MessageTooltipProps = { | ||||
+actions: $ReadOnlyArray<MessageTooltipAction>, | +actions: $ReadOnlyArray<MessageTooltipAction>, | ||||
+messageTimestamp: string, | +messageTimestamp: string, | ||||
+alignment?: 'left' | 'center' | 'right', | +tooltipPositionStyle: TooltipPositionStyle, | ||||
+tooltipSize: TooltipSize, | |||||
+item: ChatMessageInfoItem, | +item: ChatMessageInfoItem, | ||||
+threadInfo: ThreadInfo, | +threadInfo: ThreadInfo, | ||||
}; | }; | ||||
function MessageTooltip(props: MessageTooltipProps): React.Node { | function MessageTooltip(props: MessageTooltipProps): React.Node { | ||||
const { | const { | ||||
actions, | actions, | ||||
messageTimestamp, | messageTimestamp, | ||||
alignment = 'left', | tooltipPositionStyle, | ||||
tooltipSize, | |||||
item, | item, | ||||
threadInfo, | threadInfo, | ||||
} = props; | } = props; | ||||
const { messageInfo, reactions } = item; | const { messageInfo, reactions } = item; | ||||
const { alignment = 'left' } = tooltipPositionStyle; | |||||
const [activeTooltipLabel, setActiveTooltipLabel] = React.useState<?string>(); | const [activeTooltipLabel, setActiveTooltipLabel] = React.useState<?string>(); | ||||
const { renderEmojiKeyboard } = useTooltipContext(); | const { renderEmojiKeyboard } = useTooltipContext(); | ||||
const emojiKeyboardRef = React.useRef(); | |||||
const messageActionButtonsContainerClassName = classNames( | const messageActionButtonsContainerClassName = classNames( | ||||
css.messageActionContainer, | css.messageActionContainer, | ||||
css.messageActionButtons, | css.messageActionButtons, | ||||
); | ); | ||||
const messageTooltipButtonStyle = React.useMemo(() => tooltipButtonStyle, []); | const messageTooltipButtonStyle = React.useMemo(() => tooltipButtonStyle, []); | ||||
const tooltipButtons = React.useMemo(() => { | const tooltipButtons = React.useMemo(() => { | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | const tooltipTimestamp = React.useMemo(() => { | ||||
} | } | ||||
return ( | return ( | ||||
<div className={css.messageTooltipLabel} style={messageTooltipLabelStyle}> | <div className={css.messageTooltipLabel} style={messageTooltipLabelStyle}> | ||||
{messageTimestamp} | {messageTimestamp} | ||||
</div> | </div> | ||||
); | ); | ||||
}, [messageTimestamp, messageTooltipLabelStyle]); | }, [messageTimestamp, messageTooltipLabelStyle]); | ||||
const emojiKeyboardPosition = React.useMemo(() => { | |||||
return getEmojiKeyboardPosition( | |||||
emojiKeyboardRef.current, | |||||
tooltipPositionStyle, | |||||
tooltipSize, | |||||
); | |||||
// eslint-disable-next-line react-hooks/exhaustive-deps | |||||
}, [tooltipPositionStyle, tooltipSize, emojiKeyboardRef.current]); | |||||
const emojiKeyboardPositionStyle = React.useMemo( | |||||
() => ({ | |||||
bottom: emojiKeyboardPosition.bottom, | |||||
left: emojiKeyboardPosition.left, | |||||
}), | |||||
[emojiKeyboardPosition.bottom, emojiKeyboardPosition.left], | |||||
); | |||||
const nextLocalID = useSelector(state => state.nextLocalID); | const nextLocalID = useSelector(state => state.nextLocalID); | ||||
const localID = `${localIDPrefix}${nextLocalID}`; | const localID = `${localIDPrefix}${nextLocalID}`; | ||||
const sendReaction = useSendReaction(messageInfo.id, localID, threadInfo.id); | const sendReaction = useSendReaction(messageInfo.id, localID, threadInfo.id); | ||||
const onEmojiSelect = React.useCallback( | const onEmojiSelect = React.useCallback( | ||||
emoji => { | emoji => { | ||||
const reactionInput = emoji.native; | const reactionInput = emoji.native; | ||||
const viewerReacted = !!reactions.get(reactionInput)?.viewerReacted; | const viewerReacted = !!reactions.get(reactionInput)?.viewerReacted; | ||||
const action = viewerReacted ? 'remove_reaction' : 'add_reaction'; | const action = viewerReacted ? 'remove_reaction' : 'add_reaction'; | ||||
sendReaction(reactionInput, action); | sendReaction(reactionInput, action); | ||||
}, | }, | ||||
[sendReaction, reactions], | [sendReaction, reactions], | ||||
); | ); | ||||
const emojiKeyboard = React.useMemo(() => { | const emojiKeyboard = React.useMemo(() => { | ||||
if (!renderEmojiKeyboard) { | if (!renderEmojiKeyboard) { | ||||
return null; | return null; | ||||
} | } | ||||
return <Picker data={data} onEmojiSelect={onEmojiSelect} />; | |||||
}, [onEmojiSelect, renderEmojiKeyboard]); | return ( | ||||
<div | |||||
ref={emojiKeyboardRef} | |||||
style={emojiKeyboardPositionStyle} | |||||
className={css.emojiKeyboard} | |||||
> | |||||
<Picker data={data} onEmojiSelect={onEmojiSelect} /> | |||||
</div> | |||||
); | |||||
}, [emojiKeyboardPositionStyle, onEmojiSelect, renderEmojiKeyboard]); | |||||
const messageTooltipContainerStyle = React.useMemo(() => tooltipStyle, []); | const messageTooltipContainerStyle = React.useMemo(() => tooltipStyle, []); | ||||
const containerClassName = classNames({ | const containerClassName = classNames({ | ||||
[css.container]: true, | |||||
[css.containerLeftAlign]: alignment === 'left', | |||||
[css.containerCenterAlign]: alignment === 'center', | |||||
}); | |||||
const messageTooltipContainerClassNames = classNames({ | |||||
[css.messageTooltipContainer]: true, | [css.messageTooltipContainer]: true, | ||||
[css.leftTooltipAlign]: alignment === 'left', | [css.leftTooltipAlign]: alignment === 'left', | ||||
[css.centerTooltipAlign]: alignment === 'center', | [css.centerTooltipAlign]: alignment === 'center', | ||||
[css.rightTooltipAlign]: alignment === 'right', | [css.rightTooltipAlign]: alignment === 'right', | ||||
}); | }); | ||||
return ( | return ( | ||||
<div className={containerClassName}> | <> | ||||
{emojiKeyboard} | {emojiKeyboard} | ||||
<div | <div className={containerClassName} style={messageTooltipContainerStyle}> | ||||
className={messageTooltipContainerClassNames} | |||||
style={messageTooltipContainerStyle} | |||||
> | |||||
<div style={messageTooltipTopLabelStyle}>{tooltipLabel}</div> | <div style={messageTooltipTopLabelStyle}>{tooltipLabel}</div> | ||||
{tooltipButtons} | {tooltipButtons} | ||||
{tooltipTimestamp} | {tooltipTimestamp} | ||||
</div> | </div> | ||||
</div> | </> | ||||
); | ); | ||||
} | } | ||||
export default MessageTooltip; | export default MessageTooltip; |