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 { shouldRenderEmojiKeyboard } = useTooltipContext(); | ||||
const [emojiKeyboardPosition, setEmojiKeyboardPosition] = | |||||
React.useState(null); | |||||
const emojiKeyboardRef = React.useCallback( | |||||
node => { | |||||
if (node) { | |||||
ashoat: Have you considered adding a `sleep` here and just waiting for the keyboard to draw on-screen? | |||||
ginsuAuthorUnsubmitted Done Inline ActionsI did try adding a sleep when I was tinkering around, and it does work with the emoji keyboard initially rendering in the incorrect position then after a split second rendering in the correct position. I thought that this was a pretty bad solution. ginsu: I did try adding a sleep when I was tinkering around, and it does work with the emoji keyboard… | |||||
const position = getEmojiKeyboardPosition( | |||||
node, | |||||
tooltipPositionStyle, | |||||
tooltipSize, | |||||
); | |||||
setEmojiKeyboardPosition(position); | |||||
} | |||||
}, | |||||
[tooltipPositionStyle, tooltipSize], | |||||
); | |||||
const messageActionButtonsContainerClassName = classNames( | const messageActionButtonsContainerClassName = classNames( | ||||
css.messageActionContainer, | css.messageActionContainer, | ||||
css.messageActionButtons, | css.messageActionButtons, | ||||
); | ); | ||||
const messageTooltipButtonStyle = React.useMemo(() => tooltipButtonStyle, []); | const messageTooltipButtonStyle = React.useMemo(() => tooltipButtonStyle, []); | ||||
▲ Show 20 Lines • Show All 57 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 emojiKeyboardPositionStyle = React.useMemo(() => { | |||||
if (!emojiKeyboardPosition) { | |||||
return null; | |||||
} | |||||
return { | |||||
bottom: emojiKeyboardPosition.bottom, | |||||
left: emojiKeyboardPosition.left, | |||||
}; | |||||
}, [emojiKeyboardPosition]); | |||||
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[reactionInput] | const viewerReacted = reactions[reactionInput] | ||||
? reactions[reactionInput].viewerReacted | ? reactions[reactionInput].viewerReacted | ||||
: false; | : false; | ||||
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 (!shouldRenderEmojiKeyboard) { | ||||
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, | |||||
emojiKeyboardRef, | |||||
onEmojiSelect, | |||||
shouldRenderEmojiKeyboard, | |||||
]); | |||||
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; |
Have you considered adding a sleep here and just waiting for the keyboard to draw on-screen? It seems bad but slightly better than hardcoding the width / height, which will likely cause a bug next time this library is updated