Skip to content

Commit

Permalink
Adds new useSecurityAssistantOverlay hook for programmatically openin…
Browse files Browse the repository at this point in the history
…g overlay with conversationId and promptContextId
  • Loading branch information
spong committed May 12, 2023
1 parent 07ac4f3 commit a3ddec0
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
* 2.0.
*/

import React, { useCallback, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { EuiModal, EuiSpacer } from '@elastic/eui';

import useEvent from 'react-use/lib/useEvent';
import styled from 'styled-components';
import { SecurityAssistant } from '../security_assistant';
import { QuickPrompts } from './quick_prompts';
import type { ShowAssistantOverlayProps } from '../security_assistant_context';
import { useSecurityAssistantContext } from '../security_assistant_context';

const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0;

Expand All @@ -30,6 +32,27 @@ interface AssistantOverlayProps {}
export const AssistantOverlay: React.FC<AssistantOverlayProps> = React.memo(({}) => {
const [isModalVisible, setIsModalVisible] = useState(false);
const [input, setInput] = useState<string | undefined>();
const [conversationId, setConversationId] = useState<string | undefined>('default');
const [promptContextId, setPromptContextId] = useState<string | undefined>();
const { setShowAssistantOverlay } = useSecurityAssistantContext();

// Bind `showAssistantOverlay` in SecurityAssistantContext to this modal instance
const showOverlay = useCallback(
() =>
({
showOverlay: so,
promptContextId: pid,
conversationId: cid,
}: ShowAssistantOverlayProps) => {
setIsModalVisible(so);
setPromptContextId(pid);
setConversationId(cid);
},
[setIsModalVisible]
);
useEffect(() => {
setShowAssistantOverlay(showOverlay);
}, [setShowAssistantOverlay, showOverlay]);

// Register keyboard listener to show the modal when cmd + / is pressed
const onKeyDown = useCallback(
Expand All @@ -46,6 +69,8 @@ export const AssistantOverlay: React.FC<AssistantOverlayProps> = React.memo(({})
// Modal control functions
const cleanupAndCloseModal = useCallback(() => {
setIsModalVisible(false);
setPromptContextId(undefined);
setConversationId('default');
}, [setIsModalVisible]);

const handleCloseModal = useCallback(() => {
Expand All @@ -56,7 +81,7 @@ export const AssistantOverlay: React.FC<AssistantOverlayProps> = React.memo(({})
<>
{isModalVisible && (
<StyledEuiModal onClose={handleCloseModal}>
<SecurityAssistant />
<SecurityAssistant conversationId={conversationId} promptContextId={promptContextId} />

<EuiSpacer size="xs" />

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useCallback } from 'react';
import { useSecurityAssistantContext } from '../security_assistant_context';

interface UserSecurityAssistantOverlayProps {
promptContextId?: string;
conversationId?: string;
}
interface UserSecurityAssistantOverlay {
showSecurityAssistantOverlay: (showOverlay: boolean) => void;
}

export const useSecurityAssistantOverlay = ({
promptContextId,
conversationId,
}: UserSecurityAssistantOverlayProps): UserSecurityAssistantOverlay => {
const { showAssistantOverlay } = useSecurityAssistantContext();

const showSecurityAssistantOverlay = useCallback(
(showOverlay: boolean) => {
showAssistantOverlay({ showOverlay, promptContextId, conversationId });
},
[conversationId, promptContextId, showAssistantOverlay]
);

return { showSecurityAssistantOverlay };
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,31 @@
* 2.0.
*/

import { EuiButtonEmpty, EuiPopover } from '@elastic/eui';
import React, { useCallback, useMemo, useState } from 'react';
import styled from 'styled-components';
import { EuiButtonEmpty } from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';

import { SecurityAssistant } from '../security_assistant';
import * as i18n from './translations';

const SecurityAssistantContainer = styled.div`
max-height: 1020px;
max-width: 600px;
`;
import { useSecurityAssistantOverlay } from '../assistant_overlay/use_security_assistant_overlay';

const NewChatComponent: React.FC<{
promptContextId: string;
}> = ({ promptContextId }) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const closePopover = () => setIsPopoverOpen(false);
const { showSecurityAssistantOverlay } = useSecurityAssistantOverlay({
promptContextId,
conversationId: 'alertSummary',
});

const onStartConversation = useCallback(() => setIsPopoverOpen((isOpen) => !isOpen), []);
const showOverlay = useCallback(() => {
showSecurityAssistantOverlay(true);
}, [showSecurityAssistantOverlay]);

const NewChatButton = useMemo(
return useMemo(
() => (
<EuiButtonEmpty onClick={onStartConversation} iconType="discuss">
<EuiButtonEmpty onClick={showOverlay} iconType="discuss">
{i18n.NEW_CHAT}
</EuiButtonEmpty>
),
[onStartConversation]
);

return (
<EuiPopover
button={NewChatButton}
closePopover={closePopover}
isOpen={isPopoverOpen}
panelPaddingSize="none"
>
<SecurityAssistantContainer>
<SecurityAssistant promptContextId={promptContextId} conversationId={'alertSummary'} />
</SecurityAssistantContainer>
</EuiPopover>
[showOverlay]
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import type { EuiCommentProps } from '@elastic/eui';
import {
Expand Down Expand Up @@ -85,8 +85,10 @@ export const SecurityAssistant: React.FC<SecurityAssistantProps> =
const { isLoading, sendMessages } = useSendMessages();

const [selectedConversationId, setSelectedConversationId] = useState<string>(conversationId);
const currentConversation =
conversations[selectedConversationId] ?? createConversation({ conversationId });
const currentConversation = useMemo(
() => conversations[selectedConversationId] ?? createConversation({ conversationId }),
[conversationId, conversations, createConversation, selectedConversationId]
);

const { cases } = useKibana().services;
const bottomRef = useRef<HTMLDivElement | null>(null);
Expand Down Expand Up @@ -217,7 +219,8 @@ export const SecurityAssistant: React.FC<SecurityAssistantProps> =
////

useEffect(() => {
if (currentConversation.messages.length > 0) {
// Adding `conversationId !== selectedConversationId` to prevent auto-run still executing after changing selected conversation
if (currentConversation.messages.length || conversationId !== selectedConversationId) {
return;
}

Expand All @@ -232,7 +235,14 @@ export const SecurityAssistant: React.FC<SecurityAssistantProps> =
};

autoRunOnOpen();
}, [currentConversation.messages, promptContexts, promptContextId, handleSendMessage]);
}, [
currentConversation.messages,
promptContexts,
promptContextId,
handleSendMessage,
conversationId,
selectedConversationId,
]);

return (
<EuiPanel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ import type { Conversation } from './types';
import { DEFAULT_CONVERSATION_STATE } from '../use_conversation';
import { useLocalStorage } from '../../common/components/local_storage';

export interface ShowAssistantOverlayProps {
showOverlay: boolean;
promptContextId?: string;
conversationId?: string;
}

type ShowAssistantOverlay = ({
showOverlay,
promptContextId,
conversationId,
}: ShowAssistantOverlayProps) => void;
interface SecurityAssistantProviderProps {
apiConfig: SecurityAssistantUiSettings;
children: React.ReactNode;
Expand All @@ -35,6 +46,8 @@ interface UseSecurityAssistantContext {
conversationIds: string[];
conversations: Record<string, Conversation>;
setConversations: React.Dispatch<React.SetStateAction<Record<string, Conversation>>>;
showAssistantOverlay: ShowAssistantOverlay;
setShowAssistantOverlay: (showAssistantOverlay: ShowAssistantOverlay) => void;
}

export const SECURITY_ASSISTANT_UI_SETTING_KEY = 'securityAssistant';
Expand Down Expand Up @@ -84,6 +97,13 @@ export const SecurityAssistantProvider: React.FC<SecurityAssistantProviderProps>
},
});

/**
* Global Assistant Overlay actions
*/
const [showAssistantOverlay, setShowAssistantOverlay] = useState<ShowAssistantOverlay>(
(showAssistant) => {}
);

const value = useMemo(
() => ({
apiConfig,
Expand All @@ -94,6 +114,8 @@ export const SecurityAssistantProvider: React.FC<SecurityAssistantProviderProps>
setConversations,
conversationIds: Object.keys(conversations).sort(),
conversations,
showAssistantOverlay,
setShowAssistantOverlay,
}),
[
apiConfig,
Expand All @@ -103,6 +125,8 @@ export const SecurityAssistantProvider: React.FC<SecurityAssistantProviderProps>
unRegisterPromptContext,
setConversations,
conversations,
showAssistantOverlay,
setShowAssistantOverlay,
]
);

Expand Down

0 comments on commit a3ddec0

Please sign in to comment.