Copilot Chat: Make message input box input persistent per chat within the UI (#1302)

### Motivation and Context
Bug: When you enter something into the message input box in Copilot Chat
but do not send it, and you switch to another chat session, the message
will appear in the other chat session's message input box.
Expected: You type a message in one of the chat sessions, the message
should only appear in the input box of that chat session. And when you
switch to another session, the input box should become empty or show
whatever is already in there previously.

### Description
1. Remove the feature that enables users to go back to the previous
message by pressing the up arrow key.
2. Fix the bug described above to enable the expected behavior.
This commit is contained in:
Tao Chen
2023-06-01 22:05:04 +00:00
committed by GitHub
parent a0225254d8
commit fa67b4c872
5 changed files with 27 additions and 13 deletions
@@ -13,6 +13,7 @@ import { DocumentImportService } from '../../libs/services/DocumentImportService
import { useAppDispatch, useAppSelector } from '../../redux/app/hooks';
import { RootState } from '../../redux/app/store';
import { addAlert } from '../../redux/features/app/appSlice';
import { editConversationInput } from '../../redux/features/conversations/conversationsSlice';
import { SpeechService } from './../../libs/services/SpeechService';
import { TypingIndicatorRenderer } from './typing-indicator/TypingIndicatorRenderer';
@@ -67,13 +68,12 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
const account = useAccount(accounts[0] || {});
const dispatch = useAppDispatch();
const [value, setValue] = React.useState('');
const [previousValue, setPreviousValue] = React.useState('');
const [recognizer, setRecognizer] = React.useState<speechSdk.SpeechRecognizer>();
const [isListening, setIsListening] = React.useState(false);
const [documentImporting, SetDocumentImporting] = React.useState(false);
const documentImportService = new DocumentImportService(process.env.REACT_APP_BACKEND_URI as string);
const documentFileRef = useRef<HTMLInputElement | null>(null);
const { selectedId } = useAppSelector((state: RootState) => state.conversations);
const { conversations, selectedId } = useAppSelector((state: RootState) => state.conversations);
React.useEffect(() => {
async function initSpeechRecognizer() {
@@ -88,6 +88,11 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
initSpeechRecognizer();
}, [instance, inProgress]);
React.useEffect(() => {
const chatState = conversations[selectedId];
setValue(chatState.input);
}, [conversations, selectedId]);
const handleSpeech = () => {
setIsListening(true);
if (recognizer) {
@@ -136,8 +141,8 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
return; // only submit if data is not empty
}
onSubmit(data);
setPreviousValue(data);
setValue('');
dispatch(editConversationInput({ id: selectedId, newInput: '' }));
} catch (error) {
const message = `Error submitting chat input: ${(error as Error).message}`;
log(message);
@@ -148,7 +153,6 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
}),
);
}
// void chat.sendTypingStopSignalAsync();
};
return (
@@ -168,19 +172,15 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
setValue(chatInput.value);
}
}}
onChange={(_event, data) => setValue(data.value)}
onChange={(_event, data) => {
setValue(data.value);
dispatch(editConversationInput({ id: selectedId, newInput: data.value }));
}}
onKeyDown={(event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
handleSubmit(value);
return;
} else if (value === '' && previousValue !== '' && event.key === 'ArrowUp') {
event.preventDefault();
setValue(previousValue);
return;
}
// void chat.sendTypingStartSignalAsync();
}}
/>
</div>
@@ -80,6 +80,7 @@ export const useChat = () => {
messages: chatMessages,
users: [loggedInUser],
botProfilePicture: getBotProfilePicture(Object.keys(conversations).length),
input: '',
};
dispatch(addConversation(newChat));
@@ -184,6 +185,7 @@ export const useChat = () => {
users: [loggedInUser],
messages: chatMessages,
botProfilePicture: getBotProfilePicture(Object.keys(loadedConversations).length),
input: '',
};
}
@@ -10,4 +10,5 @@ export interface ChatState {
messages: IChatMessage[];
botProfilePicture: string;
lastUpdatedTimestamp?: number;
input: string;
}
@@ -26,3 +26,8 @@ export interface ConversationTitleChange {
id: string;
newTitle: string;
}
export interface ConversationInputChange {
id: string;
newInput: string;
}
@@ -3,7 +3,7 @@
import { createSlice, PayloadAction, Slice } from '@reduxjs/toolkit';
import { ChatMessageState, IChatMessage } from '../../../libs/models/ChatMessage';
import { ChatState } from './ChatState';
import { Conversations, ConversationsState, ConversationTitleChange, initialState } from './ConversationsState';
import { ConversationInputChange, Conversations, ConversationsState, ConversationTitleChange, initialState } from './ConversationsState';
export const conversationsSlice: Slice<ConversationsState> = createSlice({
name: 'conversations',
@@ -19,6 +19,11 @@ export const conversationsSlice: Slice<ConversationsState> = createSlice({
state.conversations[id].lastUpdatedTimestamp = new Date().getTime();
frontLoadChat(state, id);
},
editConversationInput: (state: ConversationsState, action: PayloadAction<ConversationInputChange>) => {
const id = action.payload.id;
const newInput = action.payload.newInput;
state.conversations[id].input = newInput;
},
setSelectedConversation: (state: ConversationsState, action: PayloadAction<string>) => {
state.selectedId = action.payload;
},
@@ -52,6 +57,7 @@ export const conversationsSlice: Slice<ConversationsState> = createSlice({
export const {
setConversations,
editConversationTitle,
editConversationInput,
setSelectedConversation,
addConversation,
updateConversation,