diff --git a/web/chat/chat-thread-list-item-menu.react.js b/web/chat/chat-thread-list-item-menu.react.js
--- a/web/chat/chat-thread-list-item-menu.react.js
+++ b/web/chat/chat-thread-list-item-menu.react.js
@@ -7,7 +7,7 @@
 import type { ThreadInfo } from 'lib/types/thread-types';
 
 import Button from '../components/button.react';
-import { useThreadIsActive } from '../selectors/nav-selectors';
+import { useThreadIsActive } from '../selectors/thread-selectors';
 import SWMansionIcon from '../SWMansionIcon.react';
 import css from './chat-thread-list-item-menu.css';
 
diff --git a/web/chat/chat-thread-list-item.react.js b/web/chat/chat-thread-list-item.react.js
--- a/web/chat/chat-thread-list-item.react.js
+++ b/web/chat/chat-thread-list-item.react.js
@@ -11,7 +11,7 @@
 import {
   useOnClickThread,
   useThreadIsActive,
-} from '../selectors/nav-selectors';
+} from '../selectors/thread-selectors';
 import SWMansionIcon from '../SWMansionIcon.react';
 import ChatThreadListItemMenu from './chat-thread-list-item-menu.react';
 import ChatThreadListSeeMoreSidebars from './chat-thread-list-see-more-sidebars.react';
diff --git a/web/chat/chat-thread-list-sidebar.react.js b/web/chat/chat-thread-list-sidebar.react.js
--- a/web/chat/chat-thread-list-sidebar.react.js
+++ b/web/chat/chat-thread-list-sidebar.react.js
@@ -8,7 +8,7 @@
 import {
   useOnClickThread,
   useThreadIsActive,
-} from '../selectors/nav-selectors';
+} from '../selectors/thread-selectors';
 import ChatThreadListItemMenu from './chat-thread-list-item-menu.react';
 import css from './chat-thread-list.css';
 import SidebarItem from './sidebar-item.react';
diff --git a/web/chat/chat-thread-list.react.js b/web/chat/chat-thread-list.react.js
--- a/web/chat/chat-thread-list.react.js
+++ b/web/chat/chat-thread-list.react.js
@@ -9,7 +9,7 @@
 import Button from '../components/button.react';
 import Search from '../components/search.react';
 import { useSelector } from '../redux/redux-utils';
-import { useOnClickNewThread } from '../selectors/nav-selectors';
+import { useOnClickNewThread } from '../selectors/thread-selectors';
 import ChatThreadListItem from './chat-thread-list-item.react';
 import css from './chat-thread-list.css';
 import { ThreadListContext } from './thread-list-provider';
diff --git a/web/chat/inline-sidebar.react.js b/web/chat/inline-sidebar.react.js
--- a/web/chat/inline-sidebar.react.js
+++ b/web/chat/inline-sidebar.react.js
@@ -7,7 +7,7 @@
 import type { ThreadInfo } from 'lib/types/thread-types';
 
 import CommIcon from '../CommIcon.react';
-import { useOnClickThread } from '../selectors/nav-selectors';
+import { useOnClickThread } from '../selectors/thread-selectors';
 import css from './inline-sidebar.css';
 
 type Props = {
diff --git a/web/chat/sidebar-item.react.js b/web/chat/sidebar-item.react.js
--- a/web/chat/sidebar-item.react.js
+++ b/web/chat/sidebar-item.react.js
@@ -5,7 +5,7 @@
 
 import type { SidebarInfo } from 'lib/types/thread-types';
 
-import { useOnClickThread } from '../selectors/nav-selectors';
+import { useOnClickThread } from '../selectors/thread-selectors';
 import css from './chat-thread-list.css';
 
 type Props = {
diff --git a/web/modals/threads/sidebars/sidebar.react.js b/web/modals/threads/sidebars/sidebar.react.js
--- a/web/modals/threads/sidebars/sidebar.react.js
+++ b/web/modals/threads/sidebars/sidebar.react.js
@@ -10,7 +10,7 @@
 import Button from '../../../components/button.react';
 import { getDefaultTextMessageRules } from '../../../markdown/rules.react';
 import { useSelector } from '../../../redux/redux-utils';
-import { useOnClickThread } from '../../../selectors/nav-selectors';
+import { useOnClickThread } from '../../../selectors/thread-selectors';
 import css from './sidebars-modal.css';
 
 type Props = {
diff --git a/web/modals/threads/subchannels/subchannel.react.js b/web/modals/threads/subchannels/subchannel.react.js
--- a/web/modals/threads/subchannels/subchannel.react.js
+++ b/web/modals/threads/subchannels/subchannel.react.js
@@ -10,7 +10,7 @@
 import Button from '../../../components/button.react';
 import { getDefaultTextMessageRules } from '../../../markdown/rules.react';
 import { useSelector } from '../../../redux/redux-utils';
-import { useOnClickThread } from '../../../selectors/nav-selectors';
+import { useOnClickThread } from '../../../selectors/thread-selectors';
 import SWMansionIcon from '../../../SWMansionIcon.react';
 import css from './subchannels-modal.css';
 
diff --git a/web/selectors/nav-selectors.js b/web/selectors/nav-selectors.js
--- a/web/selectors/nav-selectors.js
+++ b/web/selectors/nav-selectors.js
@@ -1,25 +1,14 @@
 // @flow
 
 import invariant from 'invariant';
-import * as React from 'react';
-import { useDispatch } from 'react-redux';
 import { createSelector } from 'reselect';
 
 import { nonThreadCalendarFiltersSelector } from 'lib/selectors/calendar-filter-selectors';
 import { currentCalendarQuery } from 'lib/selectors/nav-selectors';
-import { createPendingSidebar } from 'lib/shared/thread-utils';
 import type { CalendarQuery } from 'lib/types/entry-types';
 import type { CalendarFilter } from 'lib/types/filter-types';
-import type {
-  ComposableMessageInfo,
-  RobotextMessageInfo,
-} from 'lib/types/message-types';
-import type { ThreadInfo } from 'lib/types/thread-types';
-
-import { getDefaultTextMessageRules } from '../markdown/rules.react';
-import { updateNavInfoActionType } from '../redux/action-types';
+
 import type { AppState } from '../redux/redux-setup';
-import { useSelector } from '../redux/redux-utils';
 import {
   type NavigationTab,
   type NavigationSettingsSection,
@@ -128,90 +117,6 @@
   },
 );
 
-function useOnClickThread(
-  thread: ?ThreadInfo,
-): (event: SyntheticEvent<HTMLElement>) => void {
-  const dispatch = useDispatch();
-  return React.useCallback(
-    (event: SyntheticEvent<HTMLElement>) => {
-      invariant(
-        thread?.id,
-        'useOnClickThread should be called with threadID set',
-      );
-      event.preventDefault();
-      const { id: threadID } = thread;
-
-      let payload;
-      if (threadID.includes('pending')) {
-        payload = {
-          chatMode: 'view',
-          activeChatThreadID: threadID,
-          pendingThread: thread,
-        };
-      } else {
-        payload = {
-          chatMode: 'view',
-          activeChatThreadID: threadID,
-        };
-      }
-
-      dispatch({ type: updateNavInfoActionType, payload });
-    },
-    [dispatch, thread],
-  );
-}
-
-function useThreadIsActive(threadID: string): boolean {
-  return useSelector(state => threadID === state.navInfo.activeChatThreadID);
-}
-
-function useOnClickPendingSidebar(
-  messageInfo: ComposableMessageInfo | RobotextMessageInfo,
-  threadInfo: ThreadInfo,
-): (event: SyntheticEvent<HTMLElement>) => void {
-  const dispatch = useDispatch();
-  const viewerID = useSelector(state => state.currentUserInfo?.id);
-  return React.useCallback(
-    (event: SyntheticEvent<HTMLElement>) => {
-      event.preventDefault();
-      if (!viewerID) {
-        return;
-      }
-      const pendingSidebarInfo = createPendingSidebar(
-        messageInfo,
-        threadInfo,
-        viewerID,
-        getDefaultTextMessageRules().simpleMarkdownRules,
-      );
-      dispatch({
-        type: updateNavInfoActionType,
-        payload: {
-          activeChatThreadID: pendingSidebarInfo.id,
-          pendingThread: pendingSidebarInfo,
-        },
-      });
-    },
-    [viewerID, messageInfo, threadInfo, dispatch],
-  );
-}
-
-function useOnClickNewThread(): (event: SyntheticEvent<HTMLElement>) => void {
-  const dispatch = useDispatch();
-  return React.useCallback(
-    (event: SyntheticEvent<HTMLElement>) => {
-      event.preventDefault();
-      dispatch({
-        type: updateNavInfoActionType,
-        payload: {
-          chatMode: 'create',
-          selectedUserList: [],
-        },
-      });
-    },
-    [dispatch],
-  );
-}
-
 function navTabSelector(state: AppState): NavigationTab {
   return state.navInfo.tab;
 }
@@ -230,10 +135,6 @@
   activeThreadSelector,
   webCalendarQuery,
   nonThreadCalendarQuery,
-  useOnClickThread,
-  useThreadIsActive,
-  useOnClickPendingSidebar,
-  useOnClickNewThread,
   navTabSelector,
   navSettingsSectionSelector,
 };
diff --git a/web/selectors/thread-selectors.js b/web/selectors/thread-selectors.js
new file mode 100644
--- /dev/null
+++ b/web/selectors/thread-selectors.js
@@ -0,0 +1,107 @@
+// @flow
+
+import invariant from 'invariant';
+import * as React from 'react';
+import { useDispatch } from 'react-redux';
+
+import { createPendingSidebar } from 'lib/shared/thread-utils';
+import type {
+  ComposableMessageInfo,
+  RobotextMessageInfo,
+} from 'lib/types/message-types';
+import type { ThreadInfo } from 'lib/types/thread-types';
+
+import { getDefaultTextMessageRules } from '../markdown/rules.react';
+import { updateNavInfoActionType } from '../redux/action-types';
+import { useSelector } from '../redux/redux-utils';
+
+function useOnClickThread(
+  thread: ?ThreadInfo,
+): (event: SyntheticEvent<HTMLElement>) => void {
+  const dispatch = useDispatch();
+  return React.useCallback(
+    (event: SyntheticEvent<HTMLElement>) => {
+      invariant(
+        thread?.id,
+        'useOnClickThread should be called with threadID set',
+      );
+      event.preventDefault();
+      const { id: threadID } = thread;
+
+      let payload;
+      if (threadID.includes('pending')) {
+        payload = {
+          chatMode: 'view',
+          activeChatThreadID: threadID,
+          pendingThread: thread,
+        };
+      } else {
+        payload = {
+          chatMode: 'view',
+          activeChatThreadID: threadID,
+        };
+      }
+
+      dispatch({ type: updateNavInfoActionType, payload });
+    },
+    [dispatch, thread],
+  );
+}
+
+function useThreadIsActive(threadID: string): boolean {
+  return useSelector(state => threadID === state.navInfo.activeChatThreadID);
+}
+
+function useOnClickPendingSidebar(
+  messageInfo: ComposableMessageInfo | RobotextMessageInfo,
+  threadInfo: ThreadInfo,
+): (event: SyntheticEvent<HTMLElement>) => void {
+  const dispatch = useDispatch();
+  const viewerID = useSelector(state => state.currentUserInfo?.id);
+  return React.useCallback(
+    (event: SyntheticEvent<HTMLElement>) => {
+      event.preventDefault();
+      if (!viewerID) {
+        return;
+      }
+      const pendingSidebarInfo = createPendingSidebar(
+        messageInfo,
+        threadInfo,
+        viewerID,
+        getDefaultTextMessageRules().simpleMarkdownRules,
+      );
+      dispatch({
+        type: updateNavInfoActionType,
+        payload: {
+          activeChatThreadID: pendingSidebarInfo.id,
+          pendingThread: pendingSidebarInfo,
+        },
+      });
+    },
+    [viewerID, messageInfo, threadInfo, dispatch],
+  );
+}
+
+function useOnClickNewThread(): (event: SyntheticEvent<HTMLElement>) => void {
+  const dispatch = useDispatch();
+  return React.useCallback(
+    (event: SyntheticEvent<HTMLElement>) => {
+      event.preventDefault();
+      dispatch({
+        type: updateNavInfoActionType,
+        payload: {
+          chatMode: 'create',
+          selectedUserList: [],
+        },
+      });
+    },
+    [dispatch],
+  );
+}
+
+export {
+  useOnClickThread,
+  useThreadIsActive,
+  useOnClickPendingSidebar,
+  useOnClickNewThread,
+};
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
@@ -28,7 +28,7 @@
 import {
   useOnClickPendingSidebar,
   useOnClickThread,
-} from '../selectors/nav-selectors';
+} from '../selectors/thread-selectors';
 import { calculateMaxTextWidth } from '../utils/text-utils';
 
 export const tooltipPositions = Object.freeze({