diff --git a/native/input/input-state-container.react.js b/native/input/input-state-container.react.js
--- a/native/input/input-state-container.react.js
+++ b/native/input/input-state-container.react.js
@@ -5,18 +5,13 @@
 import * as React from 'react';
 import { Platform } from 'react-native';
 import { createSelector } from 'reselect';
-import uuid from 'uuid';
 
-import type {
-  SendMultimediaMessageInput,
-  SendTextMessageInput,
-} from 'lib/actions/message-actions.js';
+import type { SendMultimediaMessageInput } from 'lib/actions/message-actions.js';
 import {
   createLocalMessageActionType,
   sendMultimediaMessageActionTypes,
   sendTextMessageActionTypes,
   useSendMultimediaMessage,
-  useSendTextMessage,
 } from 'lib/actions/message-actions.js';
 import { queueReportsActionType } from 'lib/actions/report-actions.js';
 import { useNewThinThread } from 'lib/actions/thread-actions.js';
@@ -31,6 +26,7 @@
 } from 'lib/actions/upload-actions.js';
 import commStaffCommunity from 'lib/facts/comm-staff-community.js';
 import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js';
+import { useInputStateContainerSendTextMessage } from 'lib/hooks/input-state-container-hooks.js';
 import { useNewThickThread } from 'lib/hooks/thread-hooks.js';
 import type {
   CallSingleKeyserverEndpointOptions,
@@ -47,11 +43,6 @@
   combineLoadingStatuses,
   createLoadingStatusSelector,
 } from 'lib/selectors/loading-selectors.js';
-import {
-  dmOperationSpecificationTypes,
-  type OutboundDMOperationSpecification,
-} from 'lib/shared/dm-ops/dm-op-utils.js';
-import { useProcessAndSendDMOperation } from 'lib/shared/dm-ops/process-dm-ops.js';
 import {
   createMediaMessageInfo,
   useMessageCreationSideEffectsFunc,
@@ -94,7 +85,6 @@
 import {
   threadTypeIsThick,
   threadTypeIsSidebar,
-  thickThreadTypes,
 } from 'lib/types/thread-types-enum.js';
 import {
   type ClientNewThinThreadRequest,
@@ -167,10 +157,12 @@
   +sendMultimediaMessage: (
     input: SendMultimediaMessageInput,
   ) => Promise<SendMessageResult>,
-  +sendTextMessage: (input: SendTextMessageInput) => Promise<SendMessageResult>,
-  +processAndSendDMOperation: (
-    dmOperationSpecification: OutboundDMOperationSpecification,
-  ) => Promise<void>,
+  +sendTextMessage: (
+    messageInfo: RawTextMessageInfo,
+    threadInfo: ThreadInfo,
+    parentThreadInfo: ?ThreadInfo,
+    sidebarCreation: boolean,
+  ) => Promise<SendMessagePayload>,
   +newThinThread: (
     request: ClientNewThinThreadRequest,
   ) => Promise<NewThreadResult>,
@@ -468,48 +460,6 @@
     );
   };
 
-  async processAndSendTextMessageDMOperation(
-    messageInfo: RawTextMessageInfo,
-    inputThreadInfo: ThreadInfo,
-  ) {
-    void this.props.processAndSendDMOperation({
-      type: dmOperationSpecificationTypes.OUTBOUND,
-      op: {
-        type: 'send_text_message',
-        threadID: inputThreadInfo.id,
-        creatorID: messageInfo.creatorID,
-        time: Date.now(),
-        messageID: uuid.v4(),
-        text: messageInfo.text,
-      },
-      recipients: {
-        type: 'all_thread_members',
-        threadID:
-          inputThreadInfo.type === thickThreadTypes.THICK_SIDEBAR &&
-          inputThreadInfo.parentThreadID
-            ? inputThreadInfo.parentThreadID
-            : inputThreadInfo.id,
-      },
-    });
-  }
-
-  async generateAndSendTextMessageAction(
-    messageInfo: RawTextMessageInfo,
-    threadInfo: ThreadInfo,
-    parentThreadInfo: ?ThreadInfo,
-  ) {
-    if (threadTypeIsThick(threadInfo.type)) {
-      void this.processAndSendTextMessageDMOperation(messageInfo, threadInfo);
-      return;
-    }
-    void this.props.dispatchActionPromise(
-      sendTextMessageActionTypes,
-      this.sendTextMessageAction(messageInfo, threadInfo, parentThreadInfo),
-      undefined,
-      messageInfo,
-    );
-  }
-
   sendTextMessage = async (
     messageInfo: RawTextMessageInfo,
     inputThreadInfo: ThreadInfo,
@@ -527,20 +477,23 @@
     }
 
     if (!threadIsPending(inputThreadInfo.id)) {
-      void this.generateAndSendTextMessageAction(
+      void this.props.dispatchActionPromise(
+        sendTextMessageActionTypes,
+        this.sendTextMessageAction(
+          messageInfo,
+          inputThreadInfo,
+          parentThreadInfo,
+        ),
+        undefined,
         messageInfo,
-        inputThreadInfo,
-        parentThreadInfo,
       );
       return;
     }
 
-    if (!threadTypeIsThick(inputThreadInfo.type)) {
-      this.props.dispatch({
-        type: sendTextMessageActionTypes.started,
-        payload: messageInfo,
-      });
-    }
+    this.props.dispatch({
+      type: sendTextMessageActionTypes.started,
+      payload: messageInfo,
+    });
 
     let threadInfo = inputThreadInfo;
     const { viewerID } = this.props;
@@ -567,13 +520,11 @@
       const copy = cloneError(e);
       copy.localID = messageInfo.localID;
       copy.threadID = messageInfo.threadID;
-      if (!threadTypeIsThick(inputThreadInfo.type)) {
-        this.props.dispatch({
-          type: sendTextMessageActionTypes.failed,
-          payload: copy,
-          error: true,
-        });
-      }
+      this.props.dispatch({
+        type: sendTextMessageActionTypes.failed,
+        payload: copy,
+        error: true,
+      });
       return;
     } finally {
       this.pendingThreadCreations.delete(threadInfo.id);
@@ -590,10 +541,15 @@
       id: newThreadID,
     };
 
-    void this.generateAndSendTextMessageAction(
+    void this.props.dispatchActionPromise(
+      sendTextMessageActionTypes,
+      this.sendTextMessageAction(
+        newMessageInfo,
+        newThreadInfo,
+        parentThreadInfo,
+      ),
+      undefined,
       newMessageInfo,
-      newThreadInfo,
-      parentThreadInfo,
     );
   };
 
@@ -637,19 +593,14 @@
       );
       const sidebarCreation =
         this.pendingSidebarCreationMessageLocalIDs.has(localID);
-      const result = await this.props.sendTextMessage({
-        threadID: messageInfo.threadID,
-        localID,
-        text: messageInfo.text,
+      const result = await this.props.sendTextMessage(
+        messageInfo,
+        threadInfo,
+        parentThreadInfo,
         sidebarCreation,
-      });
+      );
       this.pendingSidebarCreationMessageLocalIDs.delete(localID);
-      return {
-        localID,
-        serverID: result.id,
-        threadID: messageInfo.threadID,
-        time: result.time,
-      };
+      return result;
     } catch (e) {
       const copy = cloneError(e);
       copy.localID = messageInfo.localID;
@@ -1810,7 +1761,7 @@
     const callUploadMultimedia = useLegacyAshoatKeyserverCall(uploadMultimedia);
     const callBlobServiceUpload = useBlobServiceUpload();
     const callSendMultimediaMessage = useSendMultimediaMessage();
-    const callSendTextMessage = useSendTextMessage();
+    const callSendTextMessage = useInputStateContainerSendTextMessage();
     const callNewThinThread = useNewThinThread();
     const callNewThickThread = useNewThickThread();
     const dispatchActionPromise = useDispatchActionPromise();
@@ -1819,7 +1770,6 @@
     const staffCanSee = useStaffCanSee();
     const textMessageCreationSideEffectsFunc =
       useMessageCreationSideEffectsFunc<RawTextMessageInfo>(messageTypes.TEXT);
-    const processAndSendDMOperation = useProcessAndSendDMOperation();
     const usingOlmViaTunnelbrokerForDMs = useAllowOlmViaTunnelbrokerForDMs();
 
     return (
@@ -1835,7 +1785,6 @@
         blobServiceUpload={callBlobServiceUpload}
         sendMultimediaMessage={callSendMultimediaMessage}
         sendTextMessage={callSendTextMessage}
-        processAndSendDMOperation={processAndSendDMOperation}
         newThinThread={callNewThinThread}
         newThickThread={callNewThickThread}
         dispatchActionPromise={dispatchActionPromise}
diff --git a/web/input/input-state-container.react.js b/web/input/input-state-container.react.js
--- a/web/input/input-state-container.react.js
+++ b/web/input/input-state-container.react.js
@@ -9,18 +9,13 @@
 import _memoize from 'lodash/memoize.js';
 import * as React from 'react';
 import { createSelector } from 'reselect';
-import uuid from 'uuid';
 
-import type {
-  LegacySendMultimediaMessageInput,
-  SendTextMessageInput,
-} from 'lib/actions/message-actions.js';
+import type { LegacySendMultimediaMessageInput } from 'lib/actions/message-actions.js';
 import {
   createLocalMessageActionType,
   sendMultimediaMessageActionTypes,
   sendTextMessageActionTypes,
   useLegacySendMultimediaMessage,
-  useSendTextMessage,
 } from 'lib/actions/message-actions.js';
 import { queueReportsActionType } from 'lib/actions/report-actions.js';
 import { useNewThinThread } from 'lib/actions/thread-actions.js';
@@ -41,15 +36,11 @@
 import blobService from 'lib/facts/blob-service.js';
 import commStaffCommunity from 'lib/facts/comm-staff-community.js';
 import { useAllowOlmViaTunnelbrokerForDMs } from 'lib/hooks/flag-hooks.js';
+import { useInputStateContainerSendTextMessage } from 'lib/hooks/input-state-container-hooks.js';
 import { useNewThickThread } from 'lib/hooks/thread-hooks.js';
 import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js';
 import { getNextLocalUploadID } from 'lib/media/media-utils.js';
 import { pendingToRealizedThreadIDsSelector } from 'lib/selectors/thread-selectors.js';
-import {
-  dmOperationSpecificationTypes,
-  type OutboundDMOperationSpecification,
-} from 'lib/shared/dm-ops/dm-op-utils.js';
-import { useProcessAndSendDMOperation } from 'lib/shared/dm-ops/process-dm-ops.js';
 import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
 import type { IdentityClientContextType } from 'lib/shared/identity-client-context.js';
 import {
@@ -89,7 +80,6 @@
 import type { Dispatch } from 'lib/types/redux-types.js';
 import { reportTypes } from 'lib/types/report-types.js';
 import {
-  thickThreadTypes,
   threadTypeIsSidebar,
   threadTypeIsThick,
 } from 'lib/types/thread-types-enum.js';
@@ -161,10 +151,12 @@
   +sendMultimediaMessage: (
     input: LegacySendMultimediaMessageInput,
   ) => Promise<SendMessageResult>,
-  +sendTextMessage: (input: SendTextMessageInput) => Promise<SendMessageResult>,
-  +processAndSendDMOperation: (
-    dmOperationSpecification: OutboundDMOperationSpecification,
-  ) => Promise<void>,
+  +sendTextMessage: (
+    messageInfo: RawTextMessageInfo,
+    threadInfo: ThreadInfo,
+    parentThreadInfo: ?ThreadInfo,
+    sidebarCreation: boolean,
+  ) => Promise<SendMessagePayload>,
   +newThinThread: (
     request: ClientNewThinThreadRequest,
   ) => Promise<NewThreadResult>,
@@ -1286,48 +1278,6 @@
     );
   };
 
-  async processAndSendTextMessageDMOperation(
-    messageInfo: RawTextMessageInfo,
-    inputThreadInfo: ThreadInfo,
-  ) {
-    void this.props.processAndSendDMOperation({
-      type: dmOperationSpecificationTypes.OUTBOUND,
-      op: {
-        type: 'send_text_message',
-        threadID: inputThreadInfo.id,
-        creatorID: messageInfo.creatorID,
-        time: Date.now(),
-        messageID: uuid.v4(),
-        text: messageInfo.text,
-      },
-      recipients: {
-        type: 'all_thread_members',
-        threadID:
-          inputThreadInfo.type === thickThreadTypes.THICK_SIDEBAR &&
-          inputThreadInfo.parentThreadID
-            ? inputThreadInfo.parentThreadID
-            : inputThreadInfo.id,
-      },
-    });
-  }
-
-  async generateAndSendTextMessageAction(
-    messageInfo: RawTextMessageInfo,
-    threadInfo: ThreadInfo,
-    parentThreadInfo: ?ThreadInfo,
-  ) {
-    if (threadTypeIsThick(threadInfo.type)) {
-      void this.processAndSendTextMessageDMOperation(messageInfo, threadInfo);
-      return;
-    }
-    void this.props.dispatchActionPromise(
-      sendTextMessageActionTypes,
-      this.sendTextMessageAction(messageInfo, threadInfo, parentThreadInfo),
-      undefined,
-      messageInfo,
-    );
-  }
-
   async sendTextMessage(
     messageInfo: RawTextMessageInfo,
     inputThreadInfo: ThreadInfo,
@@ -1345,20 +1295,23 @@
     }
 
     if (!threadIsPending(inputThreadInfo.id)) {
-      void this.generateAndSendTextMessageAction(
+      void this.props.dispatchActionPromise(
+        sendTextMessageActionTypes,
+        this.sendTextMessageAction(
+          messageInfo,
+          inputThreadInfo,
+          parentThreadInfo,
+        ),
+        undefined,
         messageInfo,
-        inputThreadInfo,
-        parentThreadInfo,
       );
       return;
     }
 
-    if (!threadTypeIsThick(inputThreadInfo.type)) {
-      this.props.dispatch({
-        type: sendTextMessageActionTypes.started,
-        payload: messageInfo,
-      });
-    }
+    this.props.dispatch({
+      type: sendTextMessageActionTypes.started,
+      payload: messageInfo,
+    });
 
     let threadInfo = inputThreadInfo;
     const { viewerID } = this.props;
@@ -1385,13 +1338,11 @@
       const copy = cloneError(e);
       copy.localID = messageInfo.localID;
       copy.threadID = messageInfo.threadID;
-      if (!threadTypeIsThick(inputThreadInfo.type)) {
-        this.props.dispatch({
-          type: sendTextMessageActionTypes.failed,
-          payload: copy,
-          error: true,
-        });
-      }
+      this.props.dispatch({
+        type: sendTextMessageActionTypes.failed,
+        payload: copy,
+        error: true,
+      });
       return;
     } finally {
       this.pendingThreadCreations.delete(threadInfo.id);
@@ -1407,11 +1358,15 @@
       ...threadInfo,
       id: newThreadID,
     };
-
-    void this.generateAndSendTextMessageAction(
+    void this.props.dispatchActionPromise(
+      sendTextMessageActionTypes,
+      this.sendTextMessageAction(
+        newMessageInfo,
+        newThreadInfo,
+        parentThreadInfo,
+      ),
+      undefined,
       newMessageInfo,
-      newThreadInfo,
-      parentThreadInfo,
     );
   }
 
@@ -1433,19 +1388,14 @@
       );
       const sidebarCreation =
         this.pendingSidebarCreationMessageLocalIDs.has(localID);
-      const result = await this.props.sendTextMessage({
-        threadID: messageInfo.threadID,
-        localID,
-        text: messageInfo.text,
+      const result = await this.props.sendTextMessage(
+        messageInfo,
+        threadInfo,
+        parentThreadInfo,
         sidebarCreation,
-      });
+      );
       this.pendingSidebarCreationMessageLocalIDs.delete(localID);
-      return {
-        localID,
-        serverID: result.id,
-        threadID: messageInfo.threadID,
-        time: result.time,
-      };
+      return result;
     } catch (e) {
       const copy = cloneError(e);
       copy.localID = messageInfo.localID;
@@ -1732,14 +1682,13 @@
     const callBlobServiceUpload = useBlobServiceUpload();
     const callDeleteUpload = useDeleteUpload();
     const callSendMultimediaMessage = useLegacySendMultimediaMessage();
-    const callSendTextMessage = useSendTextMessage();
+    const callSendTextMessage = useInputStateContainerSendTextMessage();
     const callNewThinThread = useNewThinThread();
     const callNewThickThread = useNewThickThread();
     const dispatch = useDispatch();
     const dispatchActionPromise = useDispatchActionPromise();
     const modalContext = useModalContext();
     const identityContext = React.useContext(IdentityClientContext);
-    const processAndSendDMOperation = useProcessAndSendDMOperation();
 
     const [sendCallbacks, setSendCallbacks] = React.useState<
       $ReadOnlyArray<() => mixed>,
@@ -1773,7 +1722,6 @@
         deleteUpload={callDeleteUpload}
         sendMultimediaMessage={callSendMultimediaMessage}
         sendTextMessage={callSendTextMessage}
-        processAndSendDMOperation={processAndSendDMOperation}
         newThinThread={callNewThinThread}
         newThickThread={callNewThickThread}
         dispatch={dispatch}