diff --git a/lib/shared/dm-ops/dm-op-utils.js b/lib/shared/dm-ops/dm-op-utils.js
--- a/lib/shared/dm-ops/dm-op-utils.js
+++ b/lib/shared/dm-ops/dm-op-utils.js
@@ -1,11 +1,14 @@
 // @flow
 
 import invariant from 'invariant';
+import _groupBy from 'lodash/fp/groupBy.js';
 import * as React from 'react';
 import uuid from 'uuid';
 
+import { type ProcessDMOperationUtilities } from './dm-op-spec.js';
 import { dmOpSpecs } from './dm-op-specs.js';
 import { useProcessAndSendDMOperation } from './process-dm-ops.js';
+import { mergeUpdatesWithMessageInfos } from '../../reducers/message-reducer.js';
 import type {
   CreateThickRawThreadInfoInput,
   DMAddMembersOperation,
@@ -13,7 +16,9 @@
   DMOperation,
   ComposableDMOperation,
 } from '../../types/dm-ops.js';
+import type { RawMessageInfo } from '../../types/message-types.js';
 import type {
+  RawThreadInfo,
   ThickRawThreadInfo,
   ThreadInfo,
 } from '../../types/minimally-encoded-thread-permissions-types.js';
@@ -26,14 +31,17 @@
   assertThickThreadType,
   thickThreadTypes,
 } from '../../types/thread-types-enum.js';
-import type { RawThreadInfos } from '../../types/thread-types.js';
+import type { LegacyRawThreadInfo } from '../../types/thread-types.js';
 import {
   type DMOperationP2PMessage,
   userActionsP2PMessageTypes,
 } from '../../types/tunnelbroker/user-actions-peer-to-peer-message-types.js';
-import type { CurrentUserInfo } from '../../types/user-types.js';
+import { updateTypes } from '../../types/update-types-enum.js';
+import type { ClientUpdateInfo } from '../../types/update-types.js';
 import { getContentSigningKey } from '../../utils/crypto-utils.js';
 import { useSelector } from '../../utils/redux-utils.js';
+import { messageSpecs } from '../messages/message-specs.js';
+import { updateSpecs } from '../updates/update-specs.js';
 
 function generateMessagesToPeers(
   message: DMOperation,
@@ -113,17 +121,17 @@
     +userID: string,
     +deviceID: string,
   }>,
-  currentUserInfo: ?CurrentUserInfo,
-  threadInfos: RawThreadInfos,
+  utilities: ProcessDMOperationUtilities,
 ): Promise<$ReadOnlyArray<OutboundP2PMessage>> {
-  if (!currentUserInfo?.id) {
+  const { viewerID, threadInfos } = utilities;
+  if (!viewerID) {
     return [];
   }
 
   let peerUserIDAndDeviceIDs = allPeerUserIDAndDeviceIDs;
   if (recipients.type === 'self_devices') {
     peerUserIDAndDeviceIDs = allPeerUserIDAndDeviceIDs.filter(
-      peer => peer.userID === currentUserInfo.id,
+      peer => peer.userID === viewerID,
     );
   } else if (recipients.type === 'some_users') {
     const userIDs = new Set(recipients.userIDs);
@@ -259,8 +267,93 @@
   );
 }
 
+function getThreadUpdatesForNewMessages(
+  rawMessageInfos: $ReadOnlyArray<RawMessageInfo>,
+  updateInfos: $ReadOnlyArray<ClientUpdateInfo>,
+  utilities: ProcessDMOperationUtilities,
+): Array<ClientUpdateInfo> {
+  const { threadInfos, viewerID } = utilities;
+
+  const { rawMessageInfos: allNewMessageInfos } = mergeUpdatesWithMessageInfos(
+    rawMessageInfos,
+    updateInfos,
+  );
+  const messagesByThreadID = _groupBy(message => message.threadID)(
+    allNewMessageInfos,
+  );
+
+  const updatedThreadInfosByThreadID: {
+    [string]: RawThreadInfo | LegacyRawThreadInfo,
+  } = {};
+  for (const threadID in messagesByThreadID) {
+    updatedThreadInfosByThreadID[threadID] = threadInfos[threadID];
+  }
+  for (const update of updateInfos) {
+    const updatedThreadInfo = updateSpecs[update.type].getUpdatedThreadInfo?.(
+      update,
+      updatedThreadInfosByThreadID,
+    );
+    if (updatedThreadInfo) {
+      updatedThreadInfosByThreadID[updatedThreadInfo.id] = updatedThreadInfo;
+    }
+  }
+
+  const newUpdateInfos: Array<ClientUpdateInfo> = [];
+  for (const threadID in messagesByThreadID) {
+    const repliesCountIncreasingMessages = messagesByThreadID[threadID].filter(
+      message => messageSpecs[message.type].includedInRepliesCount,
+    );
+
+    let threadInfo = updatedThreadInfosByThreadID[threadID];
+
+    if (repliesCountIncreasingMessages.length > 0) {
+      const repliesCountIncreaseTime = Math.max(
+        repliesCountIncreasingMessages.map(message => message.time),
+      );
+      const newThreadInfo = {
+        ...threadInfo,
+        repliesCount:
+          threadInfo.repliesCount + repliesCountIncreasingMessages.length,
+      };
+      newUpdateInfos.push({
+        type: updateTypes.UPDATE_THREAD,
+        id: uuid.v4(),
+        time: repliesCountIncreaseTime,
+        threadInfo: newThreadInfo,
+      });
+      threadInfo = newThreadInfo;
+    }
+
+    const messagesFromOtherPeers = messagesByThreadID[threadID].filter(
+      message => message.creatorID !== viewerID,
+    );
+    if (messagesFromOtherPeers.length === 0) {
+      continue;
+    }
+    // We take the most recent timestamp to make sure that
+    // change_thread_read_status operation older
+    // than it won't flip the status to read.
+    const time = Math.max(messagesFromOtherPeers.map(message => message.time));
+    invariant(threadInfo.thick, 'Thread should be thick');
+
+    // We aren't checking if the unread timestamp is lower than the time.
+    // We're doing this because we want to flip the thread to unread after
+    // any new message from a non-viewer.
+    newUpdateInfos.push({
+      type: updateTypes.UPDATE_THREAD_READ_STATUS,
+      id: uuid.v4(),
+      time,
+      threadID: threadInfo.id,
+      unread: true,
+    });
+  }
+
+  return newUpdateInfos;
+}
+
 export {
   createMessagesToPeersFromDMOp,
   useAddDMThreadMembers,
   getCreateThickRawThreadInfoInputFromThreadInfo,
+  getThreadUpdatesForNewMessages,
 };
diff --git a/lib/shared/dm-ops/process-dm-ops.js b/lib/shared/dm-ops/process-dm-ops.js
--- a/lib/shared/dm-ops/process-dm-ops.js
+++ b/lib/shared/dm-ops/process-dm-ops.js
@@ -1,9 +1,7 @@
 // @flow
 
 import invariant from 'invariant';
-import _groupBy from 'lodash/fp/groupBy.js';
 import * as React from 'react';
-import uuid from 'uuid';
 
 import type { ProcessDMOperationUtilities } from './dm-op-spec.js';
 import { dmOpSpecs } from './dm-op-specs.js';
@@ -13,6 +11,7 @@
   createMessagesToPeersFromDMOp,
   dmOperationSpecificationTypes,
   type OutboundComposableDMOperationSpecification,
+  getThreadUpdatesForNewMessages,
 } from './dm-op-utils.js';
 import { useProcessBlobHolders } from '../../actions/holder-actions.js';
 import {
@@ -22,7 +21,6 @@
 import { useLoggedInUserInfo } from '../../hooks/account-hooks.js';
 import { useGetLatestMessageEdit } from '../../hooks/latest-message-edit.js';
 import { useDispatchWithMetadata } from '../../hooks/ops-hooks.js';
-import { mergeUpdatesWithMessageInfos } from '../../reducers/message-reducer.js';
 import { getAllPeerUserIDAndDeviceIDs } from '../../selectors/user-selectors.js';
 import {
   usePeerToPeerCommunication,
@@ -33,16 +31,11 @@
   queueDMOpsActionType,
   dmOperationValidator,
 } from '../../types/dm-ops.js';
-import type { RawThreadInfo } from '../../types/minimally-encoded-thread-permissions-types.js';
 import type { NotificationsCreationData } from '../../types/notif-types.js';
 import type { DispatchMetadata } from '../../types/redux-types.js';
 import type { OutboundP2PMessage } from '../../types/sqlite-types.js';
-import type { LegacyRawThreadInfo } from '../../types/thread-types.js';
-import { updateTypes } from '../../types/update-types-enum.js';
 import { extractUserIDsFromPayload } from '../../utils/conversion-utils.js';
 import { useSelector, useDispatch } from '../../utils/redux-utils.js';
-import { messageSpecs } from '../messages/message-specs.js';
-import { updateSpecs } from '../updates/update-specs.js';
 
 function useSendDMOperationUtils() {
   const fetchMessage = useGetLatestMessageEdit();
@@ -67,11 +60,9 @@
   dmOperationSpecification: DMOperationSpecification,
   dmOpID: ?string,
 ) => Promise<void> {
-  const threadInfos = useSelector(state => state.threadStore.threadInfos);
   const baseUtilities = useSendDMOperationUtils();
   const dispatchWithMetadata = useDispatchWithMetadata();
   const allPeerUserIDAndDeviceIDs = useSelector(getAllPeerUserIDAndDeviceIDs);
-  const currentUserInfo = useSelector(state => state.currentUserInfo);
   const processBlobHolders = useProcessBlobHolders();
 
   const dispatch = useDispatch();
@@ -101,8 +92,7 @@
           dmOp,
           dmOperationSpecification.recipients,
           allPeerUserIDAndDeviceIDs,
-          currentUserInfo,
-          threadInfos,
+          utilities,
         );
       }
 
@@ -247,76 +237,13 @@
         .filter(Boolean);
       void processBlobHolders(holderOps);
 
-      const { rawMessageInfos: allNewMessageInfos } =
-        mergeUpdatesWithMessageInfos(rawMessageInfos, updateInfos);
-      const messagesByThreadID = _groupBy(message => message.threadID)(
-        allNewMessageInfos,
+      const newUpdateInfos = getThreadUpdatesForNewMessages(
+        rawMessageInfos,
+        updateInfos,
+        utilities,
       );
 
-      const updatedThreadInfosByThreadID: {
-        [string]: RawThreadInfo | LegacyRawThreadInfo,
-      } = {};
-      for (const threadID in messagesByThreadID) {
-        updatedThreadInfosByThreadID[threadID] = threadInfos[threadID];
-      }
-      for (const update of updateInfos) {
-        const updatedThreadInfo = updateSpecs[
-          update.type
-        ].getUpdatedThreadInfo?.(update, updatedThreadInfosByThreadID);
-        if (updatedThreadInfo) {
-          updatedThreadInfosByThreadID[updatedThreadInfo.id] =
-            updatedThreadInfo;
-        }
-      }
-
-      for (const threadID in messagesByThreadID) {
-        const repliesCountIncreasingMessages = messagesByThreadID[
-          threadID
-        ].filter(message => messageSpecs[message.type].includedInRepliesCount);
-
-        const threadInfo = updatedThreadInfosByThreadID[threadID];
-
-        if (repliesCountIncreasingMessages.length > 0) {
-          const repliesCountIncreaseTime = Math.max(
-            repliesCountIncreasingMessages.map(message => message.time),
-          );
-          updateInfos.push({
-            type: updateTypes.UPDATE_THREAD,
-            id: uuid.v4(),
-            time: repliesCountIncreaseTime,
-            threadInfo: {
-              ...threadInfo,
-              repliesCount:
-                threadInfo.repliesCount + repliesCountIncreasingMessages.length,
-            },
-          });
-        }
-
-        const messagesFromOtherPeers = messagesByThreadID[threadID].filter(
-          message => message.creatorID !== viewerID,
-        );
-        if (messagesFromOtherPeers.length === 0) {
-          continue;
-        }
-        // We take the most recent timestamp to make sure that
-        // change_thread_read_status operation older
-        // than it won't flip the status to read.
-        const time = Math.max(
-          messagesFromOtherPeers.map(message => message.time),
-        );
-        invariant(threadInfo.thick, 'Thread should be thick');
-
-        // We aren't checking if the unread timestamp is lower than the time.
-        // We're doing this because we want to flip the thread to unread after
-        // any new message from a non-viewer.
-        updateInfos.push({
-          type: updateTypes.UPDATE_THREAD_READ_STATUS,
-          id: uuid.v4(),
-          time,
-          threadID: threadInfo.id,
-          unread: true,
-        });
-      }
+      updateInfos.push(...newUpdateInfos);
 
       dispatchWithMetadata(
         {
@@ -336,8 +263,6 @@
       baseUtilities,
       dispatchWithMetadata,
       allPeerUserIDAndDeviceIDs,
-      currentUserInfo,
-      threadInfos,
       dispatch,
       processBlobHolders,
     ],
@@ -363,11 +288,9 @@
 function useSendComposableDMOperation(): (
   dmOperationSpecification: OutboundComposableDMOperationSpecification,
 ) => Promise<ProcessOutboundP2PMessagesResult> {
-  const threadInfos = useSelector(state => state.threadStore.threadInfos);
   const { getDMOpsSendingPromise } = usePeerToPeerCommunication();
   const dispatchWithMetadata = useDispatchWithMetadata();
   const allPeerUserIDAndDeviceIDs = useSelector(getAllPeerUserIDAndDeviceIDs);
-  const currentUserInfo = useSelector(state => state.currentUserInfo);
   const baseUtilities = useSendDMOperationUtils();
   const { processOutboundMessages } = usePeerToPeerCommunication();
   const localMessageInfos = useSelector(state => state.messageStore.local);
@@ -418,8 +341,7 @@
         op,
         recipients,
         allPeerUserIDAndDeviceIDs,
-        currentUserInfo,
-        threadInfos,
+        utilities,
       );
 
       const notificationsCreationData = await dmOpSpecs[
@@ -456,12 +378,10 @@
     },
     [
       allPeerUserIDAndDeviceIDs,
-      currentUserInfo,
       dispatchWithMetadata,
       getDMOpsSendingPromise,
       localMessageInfos,
       processOutboundMessages,
-      threadInfos,
       baseUtilities,
     ],
   );