mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site): isolate draft prompts per conversation (#23469)
This commit is contained in:
@@ -120,6 +120,24 @@ describe("useConversationEditingState", () => {
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("initializes with the correct draft for each chatID", () => {
|
||||
const chatA = "chat-aaa";
|
||||
const chatB = "chat-bbb";
|
||||
localStorage.setItem(`${draftInputStorageKeyPrefix}${chatA}`, "draft A");
|
||||
localStorage.setItem(`${draftInputStorageKeyPrefix}${chatB}`, "draft B");
|
||||
|
||||
// Each chatID should initialize with its own draft — this is
|
||||
// what the key={agentId} wrapper guarantees at the component
|
||||
// level (a new chatID means a full remount).
|
||||
const hookA = renderEditing(chatA);
|
||||
expect(hookA.result.current.editorInitialValue).toBe("draft A");
|
||||
hookA.unmount();
|
||||
|
||||
const hookB = renderEditing(chatB);
|
||||
expect(hookB.result.current.editorInitialValue).toBe("draft B");
|
||||
hookB.unmount();
|
||||
});
|
||||
|
||||
it("clears the draft from localStorage on successful send", async () => {
|
||||
localStorage.setItem(expectedKey, "draft to clear");
|
||||
|
||||
|
||||
@@ -92,22 +92,17 @@ export function useConversationEditingState(deps: {
|
||||
? `${draftInputStorageKeyPrefix}${chatID}`
|
||||
: null;
|
||||
const [editorInitialValue, setEditorInitialValue] = useState(() => {
|
||||
if (typeof window === "undefined" || !draftStorageKey) {
|
||||
if (!draftStorageKey) {
|
||||
return "";
|
||||
}
|
||||
return localStorage.getItem(draftStorageKey) ?? "";
|
||||
});
|
||||
|
||||
// Sync the ref with the initial draft value so callers that
|
||||
// read inputValueRef.current see the persisted draft. Uses a
|
||||
// layout effect so the value is available before paint.
|
||||
const initialSyncDone = useRef(false);
|
||||
// Sync the ref with the editor value so callers that read
|
||||
// inputValueRef.current see the persisted draft. Uses a layout
|
||||
// effect so the value is available before paint.
|
||||
useLayoutEffect(() => {
|
||||
if (!initialSyncDone.current && editorInitialValue) {
|
||||
initialSyncDone.current = true;
|
||||
(inputValueRef as React.MutableRefObject<string>).current =
|
||||
editorInitialValue;
|
||||
}
|
||||
inputValueRef.current = editorInitialValue;
|
||||
}, [editorInitialValue, inputValueRef]);
|
||||
|
||||
// -- History editing state --
|
||||
@@ -119,6 +114,14 @@ export function useConversationEditingState(deps: {
|
||||
readonly ChatMessagePart[]
|
||||
>([]);
|
||||
|
||||
// -- Queue editing state --
|
||||
const [editingQueuedMessageID, setEditingQueuedMessageID] = useState<
|
||||
number | null
|
||||
>(null);
|
||||
const [draftBeforeQueueEdit, setDraftBeforeQueueEdit] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
|
||||
const handleEditUserMessage = (
|
||||
messageId: number,
|
||||
text: string,
|
||||
@@ -145,14 +148,6 @@ export function useConversationEditingState(deps: {
|
||||
}
|
||||
};
|
||||
|
||||
// -- Queue editing state --
|
||||
const [editingQueuedMessageID, setEditingQueuedMessageID] = useState<
|
||||
number | null
|
||||
>(null);
|
||||
const [draftBeforeQueueEdit, setDraftBeforeQueueEdit] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
|
||||
const handleStartQueueEdit = (
|
||||
id: number,
|
||||
text: string,
|
||||
@@ -918,4 +913,12 @@ const AgentDetail: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default AgentDetail;
|
||||
// Keyed wrapper so that navigating between agents (changing the
|
||||
// :agentId param) fully remounts the component, resetting all
|
||||
// internal state — drafts, editing, queries — cleanly.
|
||||
const KeyedAgentDetail: FC = () => {
|
||||
const { agentId } = useParams<{ agentId: string }>();
|
||||
return <AgentDetail key={agentId} />;
|
||||
};
|
||||
|
||||
export default KeyedAgentDetail;
|
||||
|
||||
Reference in New Issue
Block a user