Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3345755
D4920.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
14 KB
Referenced Files
None
Subscribers
None
D4920.diff
View Options
diff --git a/web/chat/composed-message.react.js b/web/chat/composed-message.react.js
--- a/web/chat/composed-message.react.js
+++ b/web/chat/composed-message.react.js
@@ -9,7 +9,6 @@
} from 'react-feather';
import { type ChatMessageInfoItem } from 'lib/selectors/chat-selectors';
-import { useSidebarExistsOrCanBeCreated } from 'lib/shared/thread-utils';
import { stringForUser } from 'lib/shared/user-utils';
import { assertComposableMessageType } from 'lib/types/message-types';
import { type ThreadInfo } from 'lib/types/thread-types';
@@ -18,31 +17,33 @@
import css from './chat-message-list.css';
import FailedSend from './failed-send.react';
import { InlineSidebar } from './inline-sidebar.react';
-import MessageTooltip from './message-tooltip.react';
-import {
- type OnMessagePositionWithContainerInfo,
- type MessagePositionInfo,
-} from './position-types';
-import { tooltipPositions } from './tooltip-utils';
+import { tooltipPositions, useMessageTooltip } from './tooltip-utils';
const availableTooltipPositionsForViewerMessage = [
- tooltipPositions.RIGHT_TOP,
tooltipPositions.LEFT,
+ tooltipPositions.LEFT_BOTTOM,
+ tooltipPositions.LEFT_TOP,
+ tooltipPositions.RIGHT,
+ tooltipPositions.RIGHT_BOTTOM,
+ tooltipPositions.RIGHT_TOP,
+ tooltipPositions.BOTTOM,
+ tooltipPositions.TOP,
];
const availableTooltipPositionsForNonViewerMessage = [
- tooltipPositions.LEFT_TOP,
tooltipPositions.RIGHT,
+ tooltipPositions.RIGHT_BOTTOM,
+ tooltipPositions.RIGHT_TOP,
+ tooltipPositions.LEFT,
+ tooltipPositions.LEFT_BOTTOM,
+ tooltipPositions.LEFT_TOP,
+ tooltipPositions.BOTTOM,
+ tooltipPositions.TOP,
];
type BaseProps = {
+item: ChatMessageInfoItem,
+threadInfo: ThreadInfo,
+sendFailed: boolean,
- +setMouseOverMessagePosition: (
- messagePositionInfo: MessagePositionInfo,
- ) => void,
- +mouseOverMessagePosition?: ?OnMessagePositionWithContainerInfo,
- +canReply: boolean,
+children: React.Node,
+fixedWidth?: boolean,
+borderRadius: number,
@@ -50,10 +51,11 @@
type BaseConfig = React.Config<BaseProps, typeof ComposedMessage.defaultProps>;
type Props = {
...BaseProps,
- // Redux state
- +sidebarExistsOrCanBeCreated: boolean,
// withInputState
+inputState: ?InputState,
+ +onMouseLeave: ?() => mixed,
+ +onMouseEnter: (event: SyntheticEvent<HTMLDivElement>) => mixed,
+ +containsInlineSidebar: boolean,
};
class ComposedMessage extends React.PureComponent<Props> {
static defaultProps: { +borderRadius: number } = {
@@ -118,38 +120,8 @@
);
}
- let messageTooltip;
- if (
- this.props.mouseOverMessagePosition &&
- this.props.mouseOverMessagePosition.item.messageInfo.id === id &&
- (this.props.sidebarExistsOrCanBeCreated || this.props.canReply)
- ) {
- // eslint-disable-next-line no-unused-vars
- const availableTooltipPositions = isViewer
- ? availableTooltipPositionsForViewerMessage
- : availableTooltipPositionsForNonViewerMessage;
-
- messageTooltip = <MessageTooltip messageTimestamp="" actions={[]} />;
- }
-
- let messageTooltipLinks;
- if (messageTooltip) {
- const tooltipLinksClassName = classNames({
- [css.messageTooltipActiveArea]: true,
- [css.viewerMessageTooltipActiveArea]: isViewer,
- [css.nonViewerMessageActiveArea]: !isViewer,
- });
-
- messageTooltipLinks = (
- <div className={tooltipLinksClassName}>{messageTooltip}</div>
- );
- }
-
- const viewerTooltipLinks = isViewer ? messageTooltipLinks : null;
- const nonViewerTooltipLinks = !isViewer ? messageTooltipLinks : null;
-
let inlineSidebar = null;
- if (item.threadCreatedFromMessage) {
+ if (this.props.containsInlineSidebar && item.threadCreatedFromMessage) {
const positioning = isViewer ? 'right' : 'left';
inlineSidebar = (
<div className={css.sidebarMarginBottom}>
@@ -167,14 +139,12 @@
<div className={contentClassName}>
<div
className={messageBoxContainerClassName}
- onMouseEnter={this.onMouseEnter}
- onMouseLeave={this.onMouseLeave}
+ onMouseEnter={this.props.onMouseEnter}
+ onMouseLeave={this.props.onMouseLeave}
>
- {viewerTooltipLinks}
<div className={messageBoxClassName} style={messageBoxStyle}>
{this.props.children}
</div>
- {nonViewerTooltipLinks}
</div>
{deliveryIcon}
</div>
@@ -183,23 +153,6 @@
</React.Fragment>
);
}
-
- onMouseEnter: (event: SyntheticEvent<HTMLDivElement>) => void = event => {
- 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: () => void = () => {
- const { item } = this.props;
- this.props.setMouseOverMessagePosition({ type: 'off', item });
- };
}
type ConnectedConfig = React.Config<
@@ -208,16 +161,27 @@
>;
const ConnectedComposedMessage: React.ComponentType<ConnectedConfig> = React.memo<BaseConfig>(
function ConnectedComposedMessage(props) {
- const sidebarExistsOrCanBeCreated = useSidebarExistsOrCanBeCreated(
- props.threadInfo,
- props.item,
- );
+ const { item, threadInfo } = props;
const inputState = React.useContext(InputStateContext);
+ const isViewer = props.item.messageInfo.creator.isViewer;
+ const availablePositions = isViewer
+ ? availableTooltipPositionsForViewerMessage
+ : availableTooltipPositionsForNonViewerMessage;
+ const containsInlineSidebar = !!item.threadCreatedFromMessage;
+
+ const { onMouseLeave, onMouseEnter } = useMessageTooltip({
+ item,
+ threadInfo,
+ availablePositions,
+ });
+
return (
<ComposedMessage
{...props}
- sidebarExistsOrCanBeCreated={sidebarExistsOrCanBeCreated}
inputState={inputState}
+ onMouseLeave={onMouseLeave}
+ onMouseEnter={onMouseEnter}
+ containsInlineSidebar={containsInlineSidebar}
/>
);
},
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
@@ -39,26 +39,12 @@
}
let message;
if (item.messageInfo.type === messageTypes.TEXT) {
- message = (
- <TextMessage
- item={item}
- threadInfo={props.threadInfo}
- setMouseOverMessagePosition={props.setMouseOverMessagePosition}
- mouseOverMessagePosition={props.mouseOverMessagePosition}
- />
- );
+ message = <TextMessage item={item} threadInfo={props.threadInfo} />;
} else if (
item.messageInfo.type === messageTypes.IMAGES ||
item.messageInfo.type === messageTypes.MULTIMEDIA
) {
- message = (
- <MultimediaMessage
- item={item}
- threadInfo={props.threadInfo}
- setMouseOverMessagePosition={props.setMouseOverMessagePosition}
- mouseOverMessagePosition={props.mouseOverMessagePosition}
- />
- );
+ message = <MultimediaMessage item={item} threadInfo={props.threadInfo} />;
} else {
invariant(item.robotext, "Flow can't handle our fancy types :(");
message = (
diff --git a/web/chat/multimedia-message.react.js b/web/chat/multimedia-message.react.js
--- a/web/chat/multimedia-message.react.js
+++ b/web/chat/multimedia-message.react.js
@@ -12,18 +12,9 @@
import css from './chat-message-list.css';
import ComposedMessage from './composed-message.react';
import sendFailed from './multimedia-message-send-failed';
-import type {
- MessagePositionInfo,
- OnMessagePositionWithContainerInfo,
-} from './position-types';
-
type BaseProps = {
+item: ChatMessageInfoItem,
+threadInfo: ThreadInfo,
- +setMouseOverMessagePosition: (
- messagePositionInfo: MessagePositionInfo,
- ) => void,
- +mouseOverMessagePosition: ?OnMessagePositionWithContainerInfo,
};
type Props = {
...BaseProps,
@@ -71,9 +62,6 @@
item={item}
threadInfo={this.props.threadInfo}
sendFailed={sendFailed(item, inputState)}
- setMouseOverMessagePosition={this.props.setMouseOverMessagePosition}
- mouseOverMessagePosition={this.props.mouseOverMessagePosition}
- canReply={false}
fixedWidth={multimedia.length > 1}
borderRadius={16}
>
diff --git a/web/chat/text-message.react.js b/web/chat/text-message.react.js
--- a/web/chat/text-message.react.js
+++ b/web/chat/text-message.react.js
@@ -6,27 +6,19 @@
import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors';
import { onlyEmojiRegex } from 'lib/shared/emojis';
-import { colorIsDark, threadHasPermission } from 'lib/shared/thread-utils';
+import { colorIsDark } from 'lib/shared/thread-utils';
import { messageTypes } from 'lib/types/message-types';
-import { type ThreadInfo, threadPermissions } from 'lib/types/thread-types';
+import { type ThreadInfo } from 'lib/types/thread-types';
import Markdown from '../markdown/markdown.react';
import css from './chat-message-list.css';
import ComposedMessage from './composed-message.react';
import { MessageListContext } from './message-list-types';
-import type {
- MessagePositionInfo,
- OnMessagePositionWithContainerInfo,
-} from './position-types';
import textMessageSendFailed from './text-message-send-failed';
type Props = {
+item: ChatMessageInfoItem,
+threadInfo: ThreadInfo,
- +setMouseOverMessagePosition: (
- messagePositionInfo: MessagePositionInfo,
- ) => void,
- +mouseOverMessagePosition: ?OnMessagePositionWithContainerInfo,
};
function TextMessage(props: Props): React.Node {
invariant(
@@ -59,18 +51,11 @@
const messageListContext = React.useContext(MessageListContext);
invariant(messageListContext, 'DummyTextNode should have MessageListContext');
const rules = messageListContext.getTextMessageMarkdownRules(darkColor);
- const canReply = threadHasPermission(
- props.threadInfo,
- threadPermissions.VOICED,
- );
return (
<ComposedMessage
item={props.item}
threadInfo={props.threadInfo}
sendFailed={textMessageSendFailed(props.item)}
- setMouseOverMessagePosition={props.setMouseOverMessagePosition}
- mouseOverMessagePosition={props.mouseOverMessagePosition}
- canReply={canReply}
>
<div className={messageClassName} style={messageStyle}>
<Markdown rules={rules}>{text}</Markdown>
diff --git a/web/chat/tooltip-utils.js b/web/chat/tooltip-utils.js
--- a/web/chat/tooltip-utils.js
+++ b/web/chat/tooltip-utils.js
@@ -12,9 +12,11 @@
import { isComposableMessageType } from 'lib/types/message-types';
import type { ThreadInfo } from 'lib/types/thread-types';
import { threadPermissions } from 'lib/types/thread-types';
+import { longAbsoluteDate } from 'lib/utils/date-utils';
import CommIcon from '../CommIcon.react';
import { InputStateContext } from '../input/input-state';
+import { useSelector } from '../redux/redux-utils';
import {
useOnClickPendingSidebar,
useOnClickThread,
@@ -25,7 +27,9 @@
tooltipLabelStyle,
tooltipStyle,
} from './chat-constants';
+import MessageTooltip from './message-tooltip.react';
import type { PositionInfo } from './position-types';
+import { useTooltipContext } from './tooltip-provider';
export const tooltipPositions = Object.freeze({
LEFT: 'left',
@@ -401,6 +405,111 @@
]);
}
+type UseMessageTooltipArgs = {
+ +availablePositions: $ReadOnlyArray<TooltipPosition>,
+ +item: ChatMessageInfoItem,
+ +threadInfo: ThreadInfo,
+};
+
+type UseMessageTooltipResult = {
+ onMouseEnter: (event: SyntheticEvent<HTMLElement>) => void,
+ onMouseLeave: ?() => mixed,
+};
+
+function useMessageTooltip({
+ availablePositions,
+ item,
+ threadInfo,
+}: UseMessageTooltipArgs): UseMessageTooltipResult {
+ const [onMouseLeave, setOnMouseLeave] = React.useState<?() => mixed>(null);
+
+ const { renderTooltip } = useTooltipContext();
+ const tooltipActions = useMessageTooltipActions(item, threadInfo);
+
+ const containsInlineSidebar = !!item.threadCreatedFromMessage;
+
+ const timeZone = useSelector(state => state.timeZone);
+
+ const messageTimestamp = React.useMemo(() => {
+ const time = item.messageInfo.time;
+ return longAbsoluteDate(time, timeZone);
+ }, [item.messageInfo.time, timeZone]);
+
+ const tooltipSize = React.useMemo(() => {
+ if (typeof document === 'undefined') {
+ return {
+ width: 0,
+ height: 0,
+ };
+ }
+ const tooltipLabels = tooltipActions.map(action => action.label);
+ return calculateTooltipSize({
+ tooltipLabels,
+ timestamp: messageTimestamp,
+ });
+ }, [messageTimestamp, tooltipActions]);
+
+ const onMouseEnter = React.useCallback(
+ (event: SyntheticEvent<HTMLElement>) => {
+ if (!renderTooltip) {
+ return;
+ }
+ const rect = event.currentTarget.getBoundingClientRect();
+ const { top, bottom, left, right, height, width } = rect;
+ const messagePosition = { top, bottom, left, right, height, width };
+
+ const tooltipPosition = findTooltipPosition({
+ sourcePositionInfo: messagePosition,
+ tooltipSize,
+ availablePositions,
+ defaultPosition: availablePositions[0],
+ preventDisplayingBelowSource: containsInlineSidebar,
+ });
+ if (!tooltipPosition) {
+ return;
+ }
+
+ const tooltipPositionStyle = getMessageActionTooltipStyle({
+ tooltipPosition,
+ sourcePositionInfo: messagePosition,
+ tooltipSize: tooltipSize,
+ });
+
+ const { alignment } = tooltipPositionStyle;
+
+ const tooltip = (
+ <MessageTooltip
+ actions={tooltipActions}
+ messageTimestamp={messageTimestamp}
+ alignment={alignment}
+ />
+ );
+
+ const renderTooltipResult = renderTooltip({
+ newNode: tooltip,
+ tooltipPositionStyle,
+ });
+ if (renderTooltipResult) {
+ const { onMouseLeaveCallback: callback } = renderTooltipResult;
+ setOnMouseLeave((() => callback: () => () => mixed));
+ }
+ },
+ [
+ availablePositions,
+ containsInlineSidebar,
+ messageTimestamp,
+ renderTooltip,
+ tooltipActions,
+ tooltipSize,
+ ],
+ );
+
+ return {
+ onMouseEnter,
+ onMouseLeave,
+ };
+}
+
export {
findTooltipPosition,
calculateTooltipSize,
@@ -408,5 +517,6 @@
useMessageTooltipSidebarAction,
useMessageTooltipReplyAction,
useMessageTooltipActions,
+ useMessageTooltip,
sizeOfTooltipArrow,
};
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 23, 6:52 AM (17 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2569520
Default Alt Text
D4920.diff (14 KB)
Attached To
Mode
D4920: [web] Use new tooltip in composed message
Attached
Detach File
Event Timeline
Log In to Comment