diff --git a/public/components/chat_window_header_title.tsx b/public/components/chat_window_header_title.tsx index 096dc409..397a2725 100644 --- a/public/components/chat_window_header_title.tsx +++ b/public/components/chat_window_header_title.tsx @@ -83,6 +83,12 @@ export const ChatWindowHeaderTitle = React.memo(() => { onClick={() => { closePopover(); loadChat(undefined); + // Only show toast when previous session saved + if (!!chatContext.sessionId) { + core.services.notifications.toasts.addSuccess( + 'A new conversation is started and the previous one is saved.' + ); + } }} > New conversation diff --git a/public/components/edit_conversation_name_modal.tsx b/public/components/edit_conversation_name_modal.tsx index e513c2f0..f5679683 100644 --- a/public/components/edit_conversation_name_modal.tsx +++ b/public/components/edit_conversation_name_modal.tsx @@ -7,6 +7,7 @@ import React, { useCallback, useRef } from 'react'; import { EuiConfirmModal, EuiFieldText, EuiSpacer, EuiText } from '@elastic/eui'; import { usePatchSession } from '../hooks/use_sessions'; +import { useCore } from '../contexts/core_context'; interface EditConversationNameModalProps { onClose?: (status: 'updated' | 'cancelled' | 'errored', newTitle?: string) => void; @@ -19,8 +20,13 @@ export const EditConversationNameModal = ({ sessionId, defaultTitle, }: EditConversationNameModalProps) => { + const { + services: { + notifications: { toasts }, + }, + } = useCore(); const titleInputRef = useRef(null); - const { loading, abort, patchSession } = usePatchSession(); + const { loading, abort, patchSession, isAborted } = usePatchSession(); const handleCancel = useCallback(() => { abort(); @@ -33,12 +39,17 @@ export const EditConversationNameModal = ({ } try { await patchSession(sessionId, title); + toasts.addSuccess('This conversation was successfully updated.'); } catch (_e) { + if (isAborted()) { + return; + } onClose?.('errored'); + toasts.addDanger('There was an error. The name failed to update.'); return; } onClose?.('updated', title); - }, [onClose, sessionId, patchSession]); + }, [onClose, sessionId, patchSession, toasts.addSuccess, toasts.addDanger, isAborted]); return ( { .delete(`${ASSISTANT_API.SESSION}/${sessionId}`, { signal: abortControllerRef.current.signal, }) - .then((payload) => dispatch({ type: 'success', payload })) - .catch((error) => dispatch({ type: 'failure', error })); + .then((payload) => { + dispatch({ type: 'success', payload }); + }) + .catch((error) => { + dispatch({ type: 'failure', error }); + throw error; + }); }, [core.services.http] ); @@ -31,9 +36,12 @@ export const useDeleteSession = () => { abortControllerRef.current?.abort(); }, []); + const isAborted = useCallback(() => !!abortControllerRef.current?.signal.aborted, []); + return { ...state, abort, + isAborted, deleteSession, }; }; @@ -55,7 +63,10 @@ export const usePatchSession = () => { signal: abortControllerRef.current.signal, }) .then((payload) => dispatch({ type: 'success', payload })) - .catch((error) => dispatch({ type: 'failure', error })); + .catch((error) => { + dispatch({ type: 'failure', error }); + throw error; + }); }, [core.services.http] ); @@ -64,9 +75,12 @@ export const usePatchSession = () => { abortControllerRef.current?.abort(); }, []); + const isAborted = useCallback(() => !!abortControllerRef.current?.signal.aborted, []); + return { ...state, abort, + isAborted, patchSession, }; }; diff --git a/public/tabs/history/__tests__/chat_history_page.test.tsx b/public/tabs/history/__tests__/chat_history_page.test.tsx index 9f319db6..2f8fbbd4 100644 --- a/public/tabs/history/__tests__/chat_history_page.test.tsx +++ b/public/tabs/history/__tests__/chat_history_page.test.tsx @@ -17,6 +17,13 @@ import { ChatHistoryPage } from '../chat_history_page'; const setup = () => { const useCoreMock = { services: { + notifications: { + toasts: { + addSuccess: jest.fn(), + addDanger: jest.fn(), + addError: jest.fn(), + }, + }, sessions: { sessions$: new BehaviorSubject({ objects: [ diff --git a/public/tabs/history/delete_conversation_confirm_modal.tsx b/public/tabs/history/delete_conversation_confirm_modal.tsx index b0e66374..e3b6952c 100644 --- a/public/tabs/history/delete_conversation_confirm_modal.tsx +++ b/public/tabs/history/delete_conversation_confirm_modal.tsx @@ -8,6 +8,7 @@ import React, { useCallback } from 'react'; import { EuiConfirmModal, EuiText } from '@elastic/eui'; import { useDeleteSession } from '../../hooks/use_sessions'; +import { useCore } from '../../contexts/core_context'; interface DeleteConversationConfirmModalProps { onClose?: (status: 'canceled' | 'errored' | 'deleted') => void; @@ -18,7 +19,12 @@ export const DeleteConversationConfirmModal = ({ onClose, sessionId, }: DeleteConversationConfirmModalProps) => { - const { loading, deleteSession, abort } = useDeleteSession(); + const { + services: { + notifications: { toasts }, + }, + } = useCore(); + const { loading, deleteSession, abort, isAborted } = useDeleteSession(); const handleCancel = useCallback(() => { abort(); @@ -27,12 +33,17 @@ export const DeleteConversationConfirmModal = ({ const handleConfirm = useCallback(async () => { try { await deleteSession(sessionId); + toasts.addSuccess('The conversation was successfully deleted.'); } catch (_e) { + if (isAborted()) { + return; + } onClose?.('errored'); + toasts.addDanger('There was an error. The conversation failed to delete.'); return; } onClose?.('deleted'); - }, [onClose, deleteSession, sessionId]); + }, [onClose, deleteSession, sessionId, toasts.addSuccess, toasts.addDanger, isAborted]); return (