Skip to content

Commit 3fc66f8

Browse files
authored
[Agent Builder] Enable creating a new conversation from flyout (#241322)
1 parent 00fdd91 commit 3fc66f8

13 files changed

+104
-196
lines changed

x-pack/platform/plugins/shared/onechat/public/application/components/conversations/conversation_header.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,22 @@ import { ConversationSidebarToggle } from './conversation_sidebar/conversation_s
1818
import { ConversationTitle } from './conversation_title';
1919

2020
interface ConversationHeaderProps {
21-
isSidebarOpen: boolean;
22-
onToggleSidebar: () => void;
21+
sidebar?: {
22+
isOpen: boolean;
23+
onToggle: () => void;
24+
};
2325
}
2426

25-
export const ConversationHeader: React.FC<ConversationHeaderProps> = ({
26-
isSidebarOpen,
27-
onToggleSidebar,
28-
}) => {
27+
export const ConversationHeader: React.FC<ConversationHeaderProps> = ({ sidebar }) => {
2928
const hasActiveConversation = useHasActiveConversation();
29+
3030
return (
3131
<ConversationGrid>
32-
<ConversationLeft>
33-
<ConversationSidebarToggle isSidebarOpen={isSidebarOpen} onToggle={onToggleSidebar} />
34-
</ConversationLeft>
32+
{sidebar && (
33+
<ConversationLeft>
34+
<ConversationSidebarToggle isSidebarOpen={sidebar.isOpen} onToggle={sidebar.onToggle} />
35+
</ConversationLeft>
36+
)}
3537
{hasActiveConversation && (
3638
<>
3739
<ConversationCenter>

x-pack/platform/plugins/shared/onechat/public/application/components/conversations/conversations_view.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,11 @@ export const OnechatConversationsView: React.FC<{}> = () => {
8383
paddingSize="m"
8484
>
8585
<ConversationHeader
86-
isSidebarOpen={isSidebarOpen}
87-
onToggleSidebar={() => {
88-
setIsSidebarOpen((open) => !open);
86+
sidebar={{
87+
isOpen: isSidebarOpen,
88+
onToggle: () => {
89+
setIsSidebarOpen((open) => !open);
90+
},
8991
}}
9092
/>
9193
</KibanaPageTemplate.Header>

x-pack/platform/plugins/shared/onechat/public/application/components/conversations/new_conversation_button.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import { appPaths } from '../../utils/app_paths';
1313
import { useConversationId } from '../../context/conversation/use_conversation_id';
1414
import { useIsSendingMessage } from '../../hooks/use_is_sending_message';
1515
import { useSendMessage } from '../../context/send_message/send_message_context';
16+
import { useConversationContext } from '../../context/conversation/conversation_context';
1617

1718
export const NewConversationButton: React.FC<{}> = () => {
1819
const { createOnechatUrl } = useNavigation();
20+
const { isEmbeddedContext, setConversationId } = useConversationContext();
1921
const conversationId = useConversationId();
2022
const isNewConversation = !conversationId;
2123
const isSendingMessage = useIsSendingMessage();
@@ -27,12 +29,17 @@ export const NewConversationButton: React.FC<{}> = () => {
2729
if (isNewConversation) {
2830
cleanConversation();
2931
}
32+
if (isEmbeddedContext) {
33+
setConversationId?.(undefined);
34+
}
3035
};
3136

3237
const buttonProps = isDisabled
3338
? {
3439
disabled: true,
3540
}
41+
: isEmbeddedContext
42+
? {}
3643
: {
3744
href: createOnechatUrl(appPaths.chat.new),
3845
};

x-pack/platform/plugins/shared/onechat/public/application/context/conversation/conversation_context.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ interface ConversationContextValue {
1515
sessionTag?: string;
1616
agentId?: string;
1717
initialMessage?: string;
18-
setConversationId?: (conversationId: string) => void;
18+
setConversationId?: (conversationId?: string) => void;
1919
conversationActions: ConversationActions;
2020
}
2121

x-pack/platform/plugins/shared/onechat/public/application/context/conversation/embeddable_conversations_provider.tsx

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,16 @@
55
* 2.0.
66
*/
77

8-
import React, { useMemo, useState, useEffect, useCallback } from 'react';
8+
import React, { useMemo, useEffect, useCallback, useRef } from 'react';
99
import { I18nProvider } from '@kbn/i18n-react';
1010
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
1111
import { QueryClient, QueryClientProvider } from '@kbn/react-query';
1212
import type { EmbeddableConversationInternalProps } from '../../../embeddable/types';
1313
import { ConversationContext } from './conversation_context';
1414
import { OnechatServicesContext } from '../onechat_services_context';
1515
import { SendMessageProvider } from '../send_message/send_message_context';
16-
import { queryKeys } from '../../query_keys';
17-
import { newConversationId } from '../../utils/new_conversation';
1816
import { useConversationActions } from './use_conversation_actions';
19-
import { useResolveConversationId } from '../../hooks/use_resolve_conversation_id';
20-
import { useSaveLastConversationId } from '../../hooks/use_save_last_conversation_id';
17+
import { usePersistedConversationId } from '../../hooks/use_persisted_conversation_id';
2118

2219
interface EmbeddableConversationsProviderProps extends EmbeddableConversationInternalProps {
2320
children: React.ReactNode;
@@ -40,89 +37,89 @@ export const EmbeddableConversationsProvider: React.FC<EmbeddableConversationsPr
4037
[coreStart, services.startDependencies]
4138
);
4239

43-
const resolvedConversationId = useResolveConversationId({
44-
newConversation: contextProps.newConversation,
45-
conversationId: contextProps.conversationId,
40+
const { persistedConversationId, updatePersistedConversationId } = usePersistedConversationId({
4641
sessionTag: contextProps.sessionTag,
4742
agentId: contextProps.agentId,
4843
});
4944

50-
const [conversationId, setConversationId] = useState<string | undefined>(undefined);
45+
const hasInitializedConversationIdRef = useRef(false);
46+
47+
const setConversationId = useCallback(
48+
(id?: string) => {
49+
if (id !== persistedConversationId) {
50+
updatePersistedConversationId(id);
51+
}
52+
},
53+
[persistedConversationId, updatePersistedConversationId]
54+
);
5155

5256
const validateAndSetConversationId = useCallback(
5357
async (id: string) => {
5458
try {
5559
const conversation = await services.conversationsService.get({ conversationId: id });
56-
setConversationId(conversation ? id : undefined);
60+
setConversationId(conversation.id ?? undefined);
5761
} catch {
5862
setConversationId(undefined);
5963
}
6064
},
61-
[services.conversationsService]
65+
[services.conversationsService, setConversationId]
6266
);
6367

68+
// One-time initialization per provider instance:
69+
// - If newConversation flag is set, clears the conversation ID to start fresh.
70+
// - Otherwise, if there's a persisted conversation ID, validates and restores it.
71+
// - Otherwise, clears the conversation ID.
72+
// Guarded by hasInitializedConversationIdRef to prevent re-running on subsequent renders.
6473
useEffect(() => {
65-
if (!resolvedConversationId) {
74+
if (hasInitializedConversationIdRef.current) return;
75+
76+
if (contextProps.newConversation) {
77+
setConversationId(undefined);
78+
} else if (persistedConversationId) {
79+
validateAndSetConversationId(persistedConversationId);
80+
} else {
6681
setConversationId(undefined);
67-
return;
6882
}
69-
validateAndSetConversationId(resolvedConversationId);
70-
}, [resolvedConversationId, validateAndSetConversationId]);
71-
72-
useSaveLastConversationId({
73-
conversationId,
74-
sessionTag: contextProps.sessionTag,
75-
agentId: contextProps.agentId,
76-
});
77-
78-
const queryKey = queryKeys.conversations.byId(conversationId ?? newConversationId);
83+
hasInitializedConversationIdRef.current = true;
84+
}, [
85+
contextProps.newConversation,
86+
persistedConversationId,
87+
setConversationId,
88+
validateAndSetConversationId,
89+
]);
7990

8091
const onConversationCreated = useCallback(
8192
({ conversationId: id }: { conversationId: string }) => {
82-
// Update conversationId to show the newly created conversation in the UI
8393
setConversationId(id);
8494
},
85-
[]
86-
);
87-
88-
const onDeleteConversation = useCallback(
89-
({ isCurrentConversation }: { isCurrentConversation: boolean }) => {
90-
if (isCurrentConversation) {
91-
// For embeddable context, we can't navigate, just reset the conversation ID
92-
setConversationId(undefined);
93-
}
94-
},
95-
[]
95+
[setConversationId]
9696
);
9797

9898
const conversationActions = useConversationActions({
99-
conversationId,
100-
queryKey,
99+
conversationId: persistedConversationId,
101100
queryClient,
102101
conversationsService: services.conversationsService,
103102
onConversationCreated,
104-
onDeleteConversation,
105103
});
106104

107105
const conversationContextValue = useMemo(
108106
() => ({
109-
conversationId,
107+
conversationId: persistedConversationId,
110108
shouldStickToBottom: true,
111109
isEmbeddedContext: true,
112110
sessionTag: contextProps.sessionTag,
113111
agentId: contextProps.agentId,
114112
initialMessage: contextProps.initialMessage,
115-
setConversationId: (id: string) => {
116-
setConversationId(id);
117-
},
113+
setConversationId,
118114
conversationActions,
119115
}),
120116
[
121-
conversationId,
117+
persistedConversationId,
122118
contextProps.sessionTag,
123119
contextProps.agentId,
124120
contextProps.initialMessage,
125121
conversationActions,
122+
setConversationId,
126123
]
127124
);
128125

x-pack/platform/plugins/shared/onechat/public/application/context/conversation/routed_conversations_provider.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { useQueryClient } from '@kbn/react-query';
1212
import { ConversationContext } from './conversation_context';
1313
import type { LocationState } from '../../hooks/use_navigation';
1414
import { newConversationId } from '../../utils/new_conversation';
15-
import { queryKeys } from '../../query_keys';
1615
import { appPaths } from '../../utils/app_paths';
1716
import { useNavigation } from '../../hooks/use_navigation';
1817
import { useOnechatServices } from '../../hooks/use_onechat_service';
@@ -43,7 +42,6 @@ export const RoutedConversationsProvider: React.FC<RoutedConversationsProviderPr
4342
const { agents } = useOnechatAgents();
4443

4544
const { navigateToOnechatUrl } = useNavigation();
46-
const queryKey = queryKeys.conversations.byId(conversationId ?? newConversationId);
4745
const shouldAllowConversationRedirectRef = useRef(true);
4846
const agentIdSyncedRef = useRef(false);
4947

@@ -87,7 +85,6 @@ export const RoutedConversationsProvider: React.FC<RoutedConversationsProviderPr
8785

8886
const conversationActions = useConversationActions({
8987
conversationId,
90-
queryKey,
9188
queryClient,
9289
conversationsService,
9390
onConversationCreated,

x-pack/platform/plugins/shared/onechat/public/application/context/conversation/use_conversation_actions.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ export interface ConversationActions {
6161

6262
interface UseConversationActionsParams {
6363
conversationId?: string;
64-
queryKey: string[];
6564
queryClient: QueryClient;
6665
conversationsService: ConversationsService;
6766
onConversationCreated?: (params: { conversationId: string; title: string }) => void;
@@ -70,13 +69,13 @@ interface UseConversationActionsParams {
7069

7170
export const useConversationActions = ({
7271
conversationId,
73-
queryKey,
7472
queryClient,
7573
conversationsService,
7674
onConversationCreated,
7775
onDeleteConversation,
7876
}: UseConversationActionsParams): ConversationActions => {
7977
const [, setAgentIdStorage] = useLocalStorage<string>(storageKeys.agentId);
78+
const queryKey = queryKeys.conversations.byId(conversationId ?? newConversationId);
8079

8180
const setConversation = useCallback(
8281
(updater: (conversation?: Conversation) => Conversation) => {

x-pack/platform/plugins/shared/onechat/public/application/hooks/use_last_conversation_id.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import useLocalStorage from 'react-use/lib/useLocalStorage';
9+
import { storageKeys } from '../storage_keys';
10+
11+
interface UsePersistedConversationIdParams {
12+
sessionTag?: string;
13+
agentId?: string;
14+
}
15+
16+
export const usePersistedConversationId = ({
17+
sessionTag,
18+
agentId,
19+
}: UsePersistedConversationIdParams) => {
20+
const storageKey = storageKeys.getLastConversationKey(sessionTag, agentId);
21+
const [persistedConversationId, setPersistedConversationId, clearPersistedConversationId] =
22+
useLocalStorage<string>(storageKey);
23+
24+
const updatePersistedConversationId = (id?: string) => {
25+
if (id) {
26+
setPersistedConversationId(id);
27+
} else {
28+
clearPersistedConversationId();
29+
}
30+
};
31+
32+
return {
33+
persistedConversationId,
34+
updatePersistedConversationId,
35+
};
36+
};

x-pack/platform/plugins/shared/onechat/public/application/hooks/use_resolve_conversation_id.ts

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)