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 @@ -118,11 +118,13 @@ [threadID: string]: { [localUploadID: string]: PendingMultimediaUpload }, }, +drafts: { [threadID: string]: string }, + +textCursorPositions: { [threadID: string]: number }, }; class InputStateContainer extends React.PureComponent { state: State = { pendingUploads: {}, drafts: {}, + textCursorPositions: {}, }; replyCallbacks: Array<(message: string) => void> = []; pendingThreadCreations = new Map>(); @@ -153,8 +155,12 @@ state.pendingUploads, props, ); + const textCursorPositions = InputStateContainer.reassignToRealizedThreads( + state.textCursorPositions, + props, + ); - if (!drafts && !pendingUploads) { + if (!drafts && !pendingUploads && !textCursorPositions) { return null; } @@ -165,6 +171,9 @@ if (pendingUploads) { stateUpdate.pendingUploads = pendingUploads; } + if (textCursorPositions) { + stateUpdate.textCursorPositions = textCursorPositions; + } return stateUpdate; } @@ -451,9 +460,11 @@ createSelector( (state: State) => state.pendingUploads[threadID], (state: State) => state.drafts[threadID], + (state: State) => state.textCursorPositions[threadID], ( pendingUploads: ?{ [localUploadID: string]: PendingMultimediaUpload }, draft: ?string, + textCursorPosition: ?number, ) => { let threadPendingUploads = []; const assignedUploads = {}; @@ -473,7 +484,8 @@ return { pendingUploads: threadPendingUploads, assignedUploads, - draft: draft ? draft : '', + draft: draft ?? '', + textCursorPosition: textCursorPosition ?? 0, appendFiles: (files: $ReadOnlyArray) => this.appendFiles(threadID, files), cancelPendingUpload: (localUploadID: string) => @@ -485,6 +497,8 @@ createMultimediaMessage: (localID: number, threadInfo: ThreadInfo) => this.createMultimediaMessage(localID, threadInfo), setDraft: (newDraft: string) => this.setDraft(threadID, newDraft), + setTextCursorPosition: (newPosition: number) => + this.setTextCursorPosition(threadID, newPosition), messageHasUploadFailure: (localMessageID: string) => this.messageHasUploadFailure(assignedUploads[localMessageID]), retryMultimediaMessage: ( @@ -1066,6 +1080,18 @@ }); } + setTextCursorPosition(threadID: string, newPosition: number) { + this.setState(prevState => { + const newThreadID = this.getRealizedOrPendingThreadID(threadID); + return { + textCursorPositions: { + ...prevState.textCursorPositions, + [newThreadID]: newPosition, + }, + }; + }); + } + setProgress( threadID: string, localUploadID: string, diff --git a/web/input/input-state.js b/web/input/input-state.js --- a/web/input/input-state.js +++ b/web/input/input-state.js @@ -36,29 +36,31 @@ // This type represents the input state for a particular thread export type InputState = { - pendingUploads: $ReadOnlyArray, - assignedUploads: { + +pendingUploads: $ReadOnlyArray, + +assignedUploads: { [messageID: string]: $ReadOnlyArray, }, - draft: string, - appendFiles: (files: $ReadOnlyArray) => Promise, - cancelPendingUpload: (localUploadID: string) => void, - sendTextMessage: ( + +draft: string, + +textCursorPosition: number, + +appendFiles: (files: $ReadOnlyArray) => Promise, + +cancelPendingUpload: (localUploadID: string) => void, + +sendTextMessage: ( messageInfo: RawTextMessageInfo, threadInfo: ThreadInfo, ) => Promise, - createMultimediaMessage: (localID: number, threadInfo: ThreadInfo) => void, - setDraft: (draft: string) => void, - messageHasUploadFailure: (localMessageID: string) => boolean, - retryMultimediaMessage: ( + +createMultimediaMessage: (localID: number, threadInfo: ThreadInfo) => void, + +setDraft: (draft: string) => void, + +setTextCursorPosition: (newPosition: number) => void, + +messageHasUploadFailure: (localMessageID: string) => boolean, + +retryMultimediaMessage: ( localMessageID: string, threadInfo: ThreadInfo, ) => void, - addReply: (text: string) => void, - addReplyListener: ((message: string) => void) => void, - removeReplyListener: ((message: string) => void) => void, - registerSendCallback: (() => mixed) => void, - unregisterSendCallback: (() => mixed) => void, + +addReply: (text: string) => void, + +addReplyListener: ((message: string) => void) => void, + +removeReplyListener: ((message: string) => void) => void, + +registerSendCallback: (() => mixed) => void, + +unregisterSendCallback: (() => mixed) => void, }; const InputStateContext: React.Context = React.createContext(