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
@@ -47,14 +47,7 @@
     message = <MultimediaMessage item={item} threadInfo={props.threadInfo} />;
   } else {
     invariant(item.robotext, "Flow can't handle our fancy types :(");
-    message = (
-      <RobotextMessage
-        item={item}
-        threadInfo={props.threadInfo}
-        setMouseOverMessagePosition={props.setMouseOverMessagePosition}
-        mouseOverMessagePosition={props.mouseOverMessagePosition}
-      />
-    );
+    message = <RobotextMessage item={item} threadInfo={props.threadInfo} />;
   }
   return (
     <div className={css.message}>
diff --git a/web/chat/robotext-message.react.js b/web/chat/robotext-message.react.js
--- a/web/chat/robotext-message.react.js
+++ b/web/chat/robotext-message.react.js
@@ -1,15 +1,15 @@
 // @flow
 
-import classNames from 'classnames';
+// import classNames from 'classnames';
 import * as React from 'react';
 import { useDispatch } from 'react-redux';
 
 import { type RobotextChatMessageInfoItem } from 'lib/selectors/chat-selectors';
 import { threadInfoSelector } from 'lib/selectors/thread-selectors';
 import { splitRobotext, parseRobotextEntity } from 'lib/shared/message-utils';
-import { useSidebarExistsOrCanBeCreated } from 'lib/shared/thread-utils';
 import type { Dispatch } from 'lib/types/redux-types';
 import { type ThreadInfo } from 'lib/types/thread-types';
+import { longAbsoluteDate } from 'lib/utils/date-utils';
 
 import Markdown from '../markdown/markdown.react';
 import { linkRules } from '../markdown/rules.react';
@@ -17,37 +17,42 @@
 import { updateNavInfoActionType } from '../types/nav-types';
 import { InlineSidebar } from './inline-sidebar.react';
 import MessageTooltip from './message-tooltip.react';
-import type {
-  MessagePositionInfo,
-  OnMessagePositionWithContainerInfo,
-} from './position-types';
 import css from './robotext-message.css';
-import { tooltipPositions } from './tooltip-utils';
+import { useTooltipContext } from './tooltip-provider';
+import {
+  calculateTooltipSize,
+  findTooltipPosition,
+  getMessageActionTooltipStyle,
+  tooltipPositions,
+  useRobotextMessageTooltipActions,
+} from './tooltip-utils';
 
-// eslint-disable-next-line no-unused-vars
 const availableTooltipPositionsForRobotext = [
-  tooltipPositions.RIGHT_TOP,
-  tooltipPositions.RIGHT,
   tooltipPositions.LEFT,
+  tooltipPositions.LEFT_TOP,
+  tooltipPositions.LEFT_BOTTOM,
+  tooltipPositions.RIGHT,
+  tooltipPositions.RIGHT_TOP,
+  tooltipPositions.RIGHT_BOTTOM,
 ];
 
 type BaseProps = {
   +item: RobotextChatMessageInfoItem,
   +threadInfo: ThreadInfo,
-  +setMouseOverMessagePosition: (
-    messagePositionInfo: MessagePositionInfo,
-  ) => void,
-  +mouseOverMessagePosition: ?OnMessagePositionWithContainerInfo,
 };
 type Props = {
   ...BaseProps,
-  // Redux state
-  +sidebarExistsOrCanBeCreated: boolean,
+  +onMouseLeave: ?() => mixed,
+  +onMouseEnter: (event: SyntheticEvent<HTMLDivElement>) => mixed,
+  containsInlineSidebar: boolean,
 };
 class RobotextMessage extends React.PureComponent<Props> {
   render() {
     let inlineSidebar;
-    if (this.props.item.threadCreatedFromMessage) {
+    if (
+      this.props.containsInlineSidebar &&
+      this.props.item.threadCreatedFromMessage
+    ) {
       inlineSidebar = (
         <div className={css.sidebarMarginTop}>
           <InlineSidebar
@@ -58,40 +63,14 @@
       );
     }
 
-    const { item, sidebarExistsOrCanBeCreated } = this.props;
-    const { id } = item.messageInfo;
-    let messageTooltip;
-    if (
-      this.props.mouseOverMessagePosition &&
-      this.props.mouseOverMessagePosition.item.messageInfo.id === id &&
-      sidebarExistsOrCanBeCreated
-    ) {
-      messageTooltip = <MessageTooltip actions={[]} />;
-    }
-
-    let messageTooltipLinks;
-    if (messageTooltip) {
-      messageTooltipLinks = (
-        <div
-          className={classNames(
-            css.messageTooltipLinks,
-            css.nonViewerMessageTooltipLinks,
-          )}
-        >
-          {messageTooltip}
-        </div>
-      );
-    }
-
     return (
       <div className={css.robotextContainer}>
         <div
           className={css.innerRobotextContainer}
-          onMouseEnter={this.onMouseEnter}
-          onMouseLeave={this.onMouseLeave}
+          onMouseEnter={this.props.onMouseEnter}
+          onMouseLeave={this.props.onMouseLeave}
         >
           <span>{this.linkedRobotext()}</span>
-          {messageTooltipLinks}
         </div>
         {inlineSidebar}
       </div>
@@ -131,25 +110,7 @@
 
     return textParts;
   }
-
-  onMouseEnter = (event: SyntheticEvent<HTMLDivElement>) => {
-    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 = () => {
-    const { item } = this.props;
-    this.props.setMouseOverMessagePosition({ type: 'off', item });
-  };
 }
-
 type BaseInnerThreadEntityProps = {
   +id: string,
   +name: string,
@@ -198,14 +159,86 @@
 
 const ConnectedRobotextMessage: React.ComponentType<BaseProps> = React.memo<BaseProps>(
   function ConnectedRobotextMessage(props) {
-    const sidebarExistsOrCanBeCreated = useSidebarExistsOrCanBeCreated(
-      props.threadInfo,
-      props.item,
+    const { item, threadInfo } = props;
+    const [
+      onMouseLeaveCallback,
+      setOnMouseLeaveCallback,
+    ] = React.useState<?() => mixed>(null);
+
+    const { renderTooltip } = useTooltipContext();
+
+    const containsInlineSidebar = !!props.item.threadCreatedFromMessage;
+
+    const timeZone = useSelector(state => state.timeZone);
+
+    const messageTimestamp = React.useMemo(() => {
+      const time = props.item.messageInfo.time;
+      return longAbsoluteDate(time, timeZone);
+    }, [props.item.messageInfo.time, timeZone]);
+
+    const tooltipActions = useRobotextMessageTooltipActions(item, threadInfo);
+
+    const tooltip = React.useMemo(
+      () => (
+        <MessageTooltip
+          actions={tooltipActions}
+          messageTimestamp={messageTimestamp}
+        />
+      ),
+      [messageTimestamp, tooltipActions],
+    );
+
+    const onMouseEnter = React.useCallback(
+      (event: SyntheticEvent<HTMLDivElement>) => {
+        const rect = event.currentTarget.getBoundingClientRect();
+        const { top, bottom, left, right, height, width } = rect;
+        const messagePosition = { top, bottom, left, right, height, width };
+
+        if (renderTooltip) {
+          const tooltipLabels = tooltipActions.map(action => action.label);
+          const tooltipSize = calculateTooltipSize({
+            tooltipLabels,
+            timestamp: messageTimestamp,
+          });
+          const tooltipPosition = findTooltipPosition({
+            sourcePositionInfo: messagePosition,
+            tooltipSize,
+            availablePositions: availableTooltipPositionsForRobotext,
+            preventDisplayingBelowSource: containsInlineSidebar,
+          });
+          if (!tooltipPosition) {
+            return;
+          }
+          const tooltipPositionStyle = getMessageActionTooltipStyle({
+            tooltipPosition,
+            sourcePositionInfo: messagePosition,
+            tooltipSize: tooltipSize,
+          });
+
+          const renderTooltipResult = renderTooltip({
+            newNode: tooltip,
+            tooltipPosition: tooltipPositionStyle,
+          });
+          if (renderTooltipResult) {
+            const { onMouseLeaveCallback: callback } = renderTooltipResult;
+            setOnMouseLeaveCallback((() => callback: () => () => mixed));
+          }
+        }
+      },
+      [
+        containsInlineSidebar,
+        messageTimestamp,
+        renderTooltip,
+        tooltip,
+        tooltipActions,
+      ],
     );
     return (
       <RobotextMessage
         {...props}
-        sidebarExistsOrCanBeCreated={sidebarExistsOrCanBeCreated}
+        onMouseLeave={onMouseLeaveCallback}
+        onMouseEnter={onMouseEnter}
+        containsInlineSidebar={containsInlineSidebar}
       />
     );
   },