Skip to content

Commit cd31041

Browse files
committed
fix(playground): Fix session rename not showing messages immediately
1 parent b7852fe commit cd31041

8 files changed

Lines changed: 123 additions & 70 deletions

File tree

src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/chat-sidebar.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ interface ChatSidebarProps {
1313
currentSessionId?: string;
1414
onDeleteSession?: (sessionId: string) => void;
1515
onOpenLogs?: (sessionId: string) => void;
16+
renameLocalSession?: (oldSessionId: string, newSessionId: string) => void;
1617
}
1718

1819
export function ChatSidebar({
@@ -22,9 +23,13 @@ export function ChatSidebar({
2223
currentSessionId,
2324
onDeleteSession,
2425
onOpenLogs,
26+
renameLocalSession,
2527
}: ChatSidebarProps) {
2628
const currentFlowId = useGetFlowId();
27-
const { handleDelete } = useEditSessionInfo({ flowId: currentFlowId });
29+
const { handleDelete, handleRename } = useEditSessionInfo({
30+
flowId: currentFlowId,
31+
renameLocalSession,
32+
});
2833

2934
const sessionIds = useMemo(() => sessions, [sessions]);
3035

@@ -81,6 +86,7 @@ export function ChatSidebar({
8186
isVisible={visibleSession === session}
8287
updateVisibleSession={handleSessionClick}
8388
inspectSession={onOpenLogs}
89+
handleRename={handleRename}
8490
setActiveSession={() => {
8591
// TODO: Implement active session
8692
}}

src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/session-selector.tsx

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface SessionSelectorProps {
1818
setSelectedView?: (view: { type: string; id: string } | undefined) => void;
1919
playgroundPage?: boolean;
2020
setActiveSession?: (session: string) => void;
21+
handleRename?: (oldSessionId: string, newSessionId: string) => Promise<void>;
2122
}
2223

2324
export function SessionSelector({
@@ -32,6 +33,7 @@ export function SessionSelector({
3233
setSelectedView,
3334
playgroundPage = false,
3435
setActiveSession,
36+
handleRename,
3537
}: SessionSelectorProps) {
3638
const [isEditing, setIsEditing] = useState(false);
3739
const { mutate: updateSessionName } = useUpdateSessionName();
@@ -43,27 +45,41 @@ export function SessionSelector({
4345
setIsEditing(true);
4446
};
4547

46-
const handleRenameSave = (newSessionId: string) => {
48+
const handleRenameSave = async (newSessionId: string) => {
4749
setIsEditing(false);
4850
const trimmed = newSessionId.trim();
4951
if (!trimmed || trimmed === session) return;
50-
updateSessionName(
51-
{ old_session_id: session, new_session_id: trimmed },
52-
{
53-
onSuccess: () => {
54-
if (isVisible) {
52+
53+
// Use handleRename if provided (from sidebar), otherwise use mutation directly (from header)
54+
if (handleRename) {
55+
await handleRename(session, trimmed);
56+
updateVisibleSession(trimmed);
57+
if (
58+
selectedView?.type === "Session" &&
59+
selectedView?.id === session &&
60+
setSelectedView
61+
) {
62+
setSelectedView({ type: "Session", id: trimmed });
63+
}
64+
} else {
65+
// Wait for the mutation to complete before updating visible session
66+
await updateSessionName(
67+
{ old_session_id: session, new_session_id: trimmed },
68+
{
69+
onSuccess: () => {
70+
// Update visible session after rename is complete
5571
updateVisibleSession(trimmed);
56-
}
57-
if (
58-
selectedView?.type === "Session" &&
59-
selectedView?.id === session &&
60-
setSelectedView
61-
) {
62-
setSelectedView({ type: "Session", id: trimmed });
63-
}
72+
if (
73+
selectedView?.type === "Session" &&
74+
selectedView?.id === session &&
75+
setSelectedView
76+
) {
77+
setSelectedView({ type: "Session", id: trimmed });
78+
}
79+
},
6480
},
65-
},
66-
);
81+
);
82+
}
6783
};
6884

6985
return (

src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/hooks/use-edit-session-info.ts

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ const LOCAL_SESSIONS_STORAGE_KEY = (flowId: string) =>
99
export const useEditSessionInfo = ({
1010
flowId,
1111
dbSessions: providedDbSessions,
12+
renameLocalSession,
1213
}: {
1314
flowId?: string;
1415
dbSessions?: string[];
16+
renameLocalSession?: (oldSessionId: string, newSessionId: string) => void;
1517
}) => {
1618
const setSelectedSession = usePlaygroundStore(
1719
(state) => state.setSelectedSession,
@@ -43,34 +45,17 @@ export const useEditSessionInfo = ({
4345
}
4446
};
4547

46-
const handleRename = async (sessionId: string, newSessionId: string) => {
48+
const handleRename = async (sessionId: string, newSessionId: string) => {
4749
// Update session name via API or sessionStorage
4850
await updateSessionName({
4951
old_session_id: sessionId,
5052
new_session_id: newSessionId,
5153
});
5254

53-
// Update local sessions list if this is a local session
54-
if (flowId && isPlayground) {
55-
try {
56-
const stored = window.sessionStorage.getItem(
57-
LOCAL_SESSIONS_STORAGE_KEY(flowId),
58-
);
59-
if (stored) {
60-
const localSessions = JSON.parse(stored) as string[];
61-
const index = localSessions.indexOf(sessionId);
62-
if (index !== -1) {
63-
localSessions[index] = newSessionId;
64-
window.sessionStorage.setItem(
65-
LOCAL_SESSIONS_STORAGE_KEY(flowId),
66-
JSON.stringify(localSessions),
67-
);
68-
}
69-
}
70-
} catch (error) {
71-
console.error("Error updating local sessions:", error);
72-
}
73-
}
55+
// Update local sessions list using the provided function
56+
if (renameLocalSession) {
57+
renameLocalSession(sessionId, newSessionId);
58+
}
7459

7560
// Update selected session if the renamed session is currently selected
7661
if (flowId && sessionId === selectedSession) {

src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/hooks/use-get-add-sessions.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ interface UseGetAddSessionsProps {
1111
type UseGetAddSessionsReturnType = (props: UseGetAddSessionsProps) => {
1212
addNewSession: (allSessions?: string[]) => string;
1313
removeLocalSession: (sessionId: string) => void;
14+
renameLocalSession: (oldSessionId: string, newSessionId: string) => void;
1415
sessions: string[];
1516
fetchedSessions: string[];
1617
};
@@ -138,11 +139,22 @@ export const useGetAddSessions: UseGetAddSessionsReturnType = ({
138139
});
139140
};
140141

142+
const renameLocalSession = (oldSessionId: string, newSessionId: string) => {
143+
setLocalSessions((prev) => {
144+
const updated = new Set(prev);
145+
// Remove old session name and add new one
146+
const hadOld = updated.delete(oldSessionId);
147+
updated.add(newSessionId);
148+
return updated;
149+
});
150+
};
151+
141152
const stableSessions = useMemo(() => [...sessions], [sessions]);
142153

143154
return {
144155
addNewSession,
145156
removeLocalSession,
157+
renameLocalSession,
146158
sessions: stableSessions,
147159
fetchedSessions,
148160
};

src/frontend/src/components/core/playgroundComponent/chat-view/chat-messages/hooks/use-chat-history.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ export const useChatHistory = (visibleSession: string | null) => {
2929
queryFn: () => {
3030
// Return cached data immediately - this makes the query active and reactive
3131
// We're using useQuery purely as a subscription mechanism, not for fetching
32-
return queryClient.getQueryData<Message[]>(sessionCacheKey) || [];
32+
const cachedData = queryClient.getQueryData<Message[]>(sessionCacheKey) || [];
33+
return cachedData;
3334
},
3435
staleTime: Infinity, // Never refetch - updates come from setQueryData, not server
3536
gcTime: 5 * 60 * 1000, // Keep in cache for 5 minutes (allows cleanup of old sessions)
@@ -69,12 +70,12 @@ export const useChatHistory = (visibleSession: string | null) => {
6970
// In the default session, we show messages that have the same session_id as the flow_id
7071
// OR messages that have NO session_id (legacy behavior)
7172
if (visibleSession === currentFlowId) {
72-
return (
73-
isCurrentFlow &&
74-
(message.session_id === visibleSession || !message.session_id)
75-
);
73+
const matches = isCurrentFlow &&
74+
(message.session_id === visibleSession || !message.session_id);
75+
return matches;
7676
}
77-
return isCurrentFlow && message.session_id === visibleSession;
77+
const matches = isCurrentFlow && message.session_id === visibleSession;
78+
return matches;
7879
})
7980
.map((message: Message) => {
8081
let files = message.files;
@@ -111,7 +112,8 @@ export const useChatHistory = (visibleSession: string | null) => {
111112
};
112113
});
113114

114-
return [...filteredMessages].sort(sortSenderMessages);
115+
const sorted = [...filteredMessages].sort(sortSenderMessages);
116+
return sorted;
115117
}, [messages, visibleSession, currentFlowId]);
116118

117119
return chatHistory;

src/frontend/src/components/core/playgroundComponent/chat-view/hooks/use-send-message.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,6 @@ export const useSendMessage = ({ sessionId }: UseSendMessageProps = {}) => {
3333
}): Promise<void> => {
3434
// Resolve session: sessionId prop > selectedSession > flowId (default)
3535
const actualSession = sessionId ?? selectedSession ?? flowId;
36-
37-
console.debug("[useSendMessage] invoked", {
38-
sessionId: actualSession,
39-
chatInputId,
40-
hasFiles: Boolean(files?.length),
41-
});
42-
4336
// Add placeholder user message immediately
4437
addUserMessage({
4538
id: null,

src/frontend/src/components/core/playgroundComponent/sliding-container/components/flow-page-sliding-container.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,15 @@ export function FlowPageSlidingContainerContent({
3737
);
3838
const [openLogsModal, setOpenLogsModal] = useState(false);
3939

40-
const { sessions, addNewSession, removeLocalSession, fetchedSessions } =
40+
const { sessions, addNewSession, removeLocalSession, renameLocalSession, fetchedSessions } =
4141
useGetAddSessions({
4242
flowId: currentFlowId,
4343
currentSessionId,
4444
});
4545
const { handleDelete } = useEditSessionInfo({
4646
flowId: currentFlowId,
4747
dbSessions: fetchedSessions,
48+
renameLocalSession,
4849
});
4950

5051
// Ensure currentFlowId is always first in sessions list
@@ -157,6 +158,7 @@ export function FlowPageSlidingContainerContent({
157158
currentSessionId={currentSessionId}
158159
onDeleteSession={handleDeleteSession}
159160
onOpenLogs={handleOpenLogs}
161+
renameLocalSession={renameLocalSession}
160162
/>
161163
</div>
162164
</div>

src/frontend/src/controllers/API/queries/messages/use-rename-session.ts

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { UseMutationResult } from "@tanstack/react-query";
22
import { useGetFlowId } from "@/modals/IOModal/hooks/useGetFlowId";
33
import useFlowStore from "@/stores/flowStore";
44
import { useMessagesStore } from "@/stores/messagesStore";
5+
import { usePlaygroundStore } from "@/stores/playgroundStore";
56
import type { useMutationFunctionType } from "@/types/api";
67
import type { Message } from "@/types/messages";
78
import { api } from "../../api";
@@ -22,22 +23,23 @@ export const useUpdateSessionName: useMutationFunctionType<
2223
const flowId = useGetFlowId();
2324

2425
const updateSessionApi = async (data: UpdateSessionParams) => {
25-
const isPlayground = useFlowStore.getState().playgroundPage;
26+
const isPlaygroundFromFlow = useFlowStore.getState().playgroundPage;
27+
const isPlaygroundFromPlayground = usePlaygroundStore.getState().isPlayground;
28+
const isPlayground = isPlaygroundFromFlow || isPlaygroundFromPlayground;
2629
// if we are in playground we will edit the local storage instead of the API
2730
if (isPlayground && flowId) {
28-
const messages = JSON.parse(sessionStorage.getItem(flowId) || "[]");
31+
const messages = JSON.parse(sessionStorage.getItem(flowId) || "[]");
2932
const messagesWithNewSessionId = messages.map((message: Message) => {
3033
if (message.session_id === data.old_session_id) {
3134
message.session_id = data.new_session_id;
3235
}
3336
return message;
3437
});
35-
sessionStorage.setItem(flowId, JSON.stringify(messagesWithNewSessionId));
36-
38+
sessionStorage.setItem(flowId, JSON.stringify(messagesWithNewSessionId));
3739
// Update the messages store to reflect the new session_id
3840
useMessagesStore.getState().renameSession(data.old_session_id, data.new_session_id);
3941

40-
// Update React Query cache - move messages from old session key to new session key
42+
// CRITICAL: Move messages from old cache key to new cache key
4143
const oldCacheKey = [
4244
"useGetMessagesQuery",
4345
{ id: flowId, session_id: data.old_session_id },
@@ -47,18 +49,25 @@ export const useUpdateSessionName: useMutationFunctionType<
4749
{ id: flowId, session_id: data.new_session_id },
4850
];
4951

50-
const oldMessages = queryClient.getQueryData<Message[]>(oldCacheKey);
51-
if (oldMessages) {
52-
// Update session_id in cached messages and move to new cache key
53-
const updatedMessages = oldMessages.map((msg) => ({
54-
...msg,
55-
session_id: data.new_session_id,
56-
}));
57-
queryClient.setQueryData(newCacheKey, updatedMessages);
58-
// Remove old cache entry
52+
const oldCacheData = queryClient.getQueryData<Message[]>(oldCacheKey);
53+
// Get fresh messages from sessionStorage (source of truth after rename)
54+
const freshMessages = JSON.parse(sessionStorage.getItem(flowId) || "[]");
55+
const messagesForNewSession = freshMessages.filter(
56+
(msg: Message) => msg.session_id === data.new_session_id
57+
);
58+
59+
// Set the new cache with fresh messages from sessionStorage
60+
queryClient.setQueryData(newCacheKey, messagesForNewSession);
61+
62+
// Remove the old cache key
63+
if (oldCacheData && oldCacheData.length > 0) {
5964
queryClient.removeQueries({ queryKey: oldCacheKey });
6065
}
6166

67+
queryClient.invalidateQueries({
68+
queryKey: ["useGetSessionsFromFlowQuery"],
69+
});
70+
6271
return {
6372
data: messagesWithNewSessionId,
6473
};
@@ -70,17 +79,45 @@ export const useUpdateSessionName: useMutationFunctionType<
7079
params: { new_session_id: data.new_session_id },
7180
},
7281
);
82+
83+
// Update React Query cache with the renamed messages
84+
if (result.data && flowId) {
85+
const newCacheKey = [
86+
"useGetMessagesQuery",
87+
{ id: flowId, session_id: data.new_session_id },
88+
];
89+
90+
queryClient.setQueryData(newCacheKey, result.data);
91+
92+
// Remove old cache key
93+
const oldCacheKey = [
94+
"useGetMessagesQuery",
95+
{ id: flowId, session_id: data.old_session_id },
96+
];
97+
queryClient.removeQueries({ queryKey: oldCacheKey });
98+
}
99+
73100
return result.data;
74101
}
75102
};
76103

77104
const mutation: UseMutationResult<Message[], any, UpdateSessionParams> =
78105
mutate(["useUpdateSessionName"], updateSessionApi, {
79-
...options,
80-
onSettled: () => {
106+
onMutate: (variables) => {
107+
},
108+
onSuccess: (data, variables, context, ...rest) => {
109+
// Call the original onSuccess if provided
110+
options?.onSuccess?.(data, variables, context, ...rest);
111+
},
112+
onError: (error, variables, context, ...rest) => {
113+
options?.onError?.(error, variables, context, ...rest);
114+
},
115+
onSettled: (data, error, variables, context, ...rest) => {
81116
queryClient.invalidateQueries({
82117
queryKey: ["useGetSessionsFromFlowQuery"],
83118
});
119+
// Call the original onSettled if provided
120+
options?.onSettled?.(data, error, variables, context, ...rest);
84121
},
85122
});
86123

0 commit comments

Comments
 (0)