diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.test.tsx index d19337d3fb3fb..610354f8af822 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.test.tsx @@ -70,6 +70,7 @@ describe('AttachToActiveTimeline', () => { [TimelineId.active]: { ...mockGlobalState.timeline.timelineById[TimelineId.test], savedObjectId: 'savedObjectId', + status: 'active', }, }, }, @@ -104,6 +105,7 @@ describe('AttachToActiveTimeline', () => { [TimelineId.active]: { ...mockGlobalState.timeline.timelineById[TimelineId.test], savedObjectId: 'savedObjectId', + status: 'active', }, }, }, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.tsx index 05e3d1fef6ca5..77b3404561275 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/attach_to_active_timeline.tsx @@ -10,6 +10,7 @@ import { EuiCallOut, EuiCheckbox, EuiFlexGroup, EuiFlexItem, EuiText } from '@el import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; import { useSelector } from 'react-redux'; +import { TimelineStatusEnum } from '../../../../../common/api/timeline'; import type { State } from '../../../../common/store'; import { TimelineId } from '../../../../../common/types'; import { SaveTimelineButton } from '../../../../timelines/components/modal/actions/save_timeline_button'; @@ -76,10 +77,9 @@ export const AttachToActiveTimeline = memo( const timeline = useSelector((state: State) => timelineSelectors.selectTimelineById(state, TimelineId.active) ); - const timelineSavedObjectId = useMemo(() => timeline?.savedObjectId ?? '', [timeline]); const isTimelineSaved: boolean = useMemo( - () => timelineSavedObjectId.length > 0, - [timelineSavedObjectId] + () => timeline.status === TimelineStatusEnum.active, + [timeline.status] ); const onCheckboxChange = useCallback( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_details.test.tsx index f8be78abc3ec9..c6f8b51817de7 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_details.test.tsx @@ -24,6 +24,8 @@ import { Flyouts } from '../../shared/constants/flyouts'; import { TimelineId } from '../../../../../common/types'; import { ReqStatus } from '../../../../notes'; import { useBasicDataFromDetailsData } from '../../shared/hooks/use_basic_data_from_details_data'; +import { TimelineStatusEnum } from '../../../../../common/api/timeline'; +import type { State } from '../../../../common/store'; jest.mock('../../shared/hooks/use_which_flyout'); jest.mock('../../shared/hooks/use_basic_data_from_details_data'); @@ -52,7 +54,7 @@ const panelContextValue = { dataFormattedForFieldBrowser: [], } as unknown as DocumentDetailsContext; -const mockGlobalStateWithSavedTimeline = { +const mockGlobalStateWithSavedTimeline: State = { ...mockGlobalState, timeline: { ...mockGlobalState.timeline, @@ -61,6 +63,7 @@ const mockGlobalStateWithSavedTimeline = { [TimelineId.active]: { ...mockGlobalState.timeline.timelineById[TimelineId.test], savedObjectId: 'savedObjectId', + status: TimelineStatusEnum.active, pinnedEventIds: {}, }, }, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_details.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_details.tsx index 244b35fba238e..f97ca576d2385 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_details.tsx @@ -10,6 +10,7 @@ import { useDispatch, useSelector } from 'react-redux'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingElastic, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useBasicDataFromDetailsData } from '../../shared/hooks/use_basic_data_from_details_data'; +import type { TimelineModel } from '../../../..'; import { Flyouts } from '../../shared/constants/flyouts'; import { timelineSelectors } from '../../../../timelines/store'; import { TimelineId } from '../../../../../common/types'; @@ -21,6 +22,7 @@ import { NotesList } from '../../../../notes/components/notes_list'; import { pinEvent } from '../../../../timelines/store/actions'; import type { State } from '../../../../common/store'; import type { Note } from '../../../../../common/api/timeline'; +import { TimelineStatusEnum } from '../../../../../common/api/timeline'; import { fetchNotesByDocumentIds, ReqStatus, @@ -63,10 +65,17 @@ export const NotesDetails = memo(() => { // if the flyout is open from a timeline and that timeline is saved, we automatically check the checkbox to associate the note to it const isTimelineFlyout = useWhichFlyout() === Flyouts.timeline; - const timeline = useSelector((state: State) => + const timeline: TimelineModel = useSelector((state: State) => timelineSelectors.selectTimelineById(state, TimelineId.active) ); - const timelineSavedObjectId = useMemo(() => timeline?.savedObjectId ?? '', [timeline]); + const timelineSavedObjectId = useMemo( + () => timeline.savedObjectId ?? '', + [timeline.savedObjectId] + ); + const isTimelineSaved: boolean = useMemo( + () => timeline.status === TimelineStatusEnum.active, + [timeline.status] + ); // Automatically pin an associated event if it's attached to a timeline and it's not pinned yet const onNoteAddInTimeline = useCallback(() => { @@ -141,7 +150,7 @@ export const NotesDetails = memo(() => { {isTimelineFlyout && ( )} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.test.tsx index e70bd5946e3b1..4de3728e2290f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.test.tsx @@ -18,6 +18,8 @@ import React from 'react'; import { TimelineId } from '../../../../../../common/types'; import { SAVE_TIMELINE_CALLOUT_TEST_ID } from '../../../notes/test_ids'; import { useUserPrivileges } from '../../../../../common/components/user_privileges'; +import { TimelineStatusEnum } from '../../../../../../common/api/timeline'; +import type { State } from '../../../../../common/store'; jest.mock('../../../../../common/hooks/use_experimental_features'); jest.mock('../../../../../common/components/user_privileges'); @@ -38,7 +40,7 @@ jest.mock('react-redux', () => { }; }); -const mockGlobalStateWithSavedTimeline = { +const mockGlobalStateWithSavedTimeline: State = { ...mockGlobalState, timeline: { ...mockGlobalState.timeline, @@ -47,11 +49,12 @@ const mockGlobalStateWithSavedTimeline = { [TimelineId.active]: { ...mockGlobalState.timeline.timelineById[TimelineId.test], savedObjectId: 'savedObjectId', + status: TimelineStatusEnum.active, }, }, }, }; -const mockGlobalStateWithUnSavedTimeline = { +const mockGlobalStateWithUnSavedTimeline: State = { ...mockGlobalState, timeline: { ...mockGlobalState.timeline, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.tsx index dcc9f229b8420..737001125c990 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/notes/index.tsx @@ -21,6 +21,7 @@ import { css } from '@emotion/react'; import { useDispatch, useSelector } from 'react-redux'; import { FormattedRelative } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; +import type { TimelineModel } from '../../../../..'; import { SaveTimelineCallout } from '../../../notes/save_timeline'; import { AddNote } from '../../../../../notes/components/add_note'; import { useUserPrivileges } from '../../../../../common/components/user_privileges'; @@ -40,6 +41,7 @@ import { selectSortedNotesBySavedObjectId, } from '../../../../../notes'; import type { Note } from '../../../../../../common/api/timeline'; +import { TimelineStatusEnum } from '../../../../../../common/api/timeline'; import { NotesList } from '../../../../../notes/components/notes_list'; import { OldNotes } from '../../../notes/old_notes'; import { Participants } from '../../../notes/participants'; @@ -92,11 +94,16 @@ const NotesTabContentComponent: React.FC = React.memo(({ t const scrollToTop = useShallowEqualSelector((state) => getScrollToTop(state, timelineId)); useScrollToTop('#scrollableNotes', !!scrollToTop); - const timeline = useSelector((state: State) => selectTimelineById(state, timelineId)); - const timelineSavedObjectId = useMemo(() => timeline?.savedObjectId ?? '', [timeline]); + const timeline: TimelineModel = useSelector((state: State) => + selectTimelineById(state, timelineId) + ); + const timelineSavedObjectId = useMemo( + () => timeline.savedObjectId ?? '', + [timeline.savedObjectId] + ); const isTimelineSaved: boolean = useMemo( - () => timelineSavedObjectId.length > 0, - [timelineSavedObjectId] + () => timeline.status === TimelineStatusEnum.active, + [timeline.status] ); const fetchNotes = useCallback(