From 1a3353e8d072f201f97de8eaf8411124a5530929 Mon Sep 17 00:00:00 2001 From: Henry Fontanier Date: Tue, 3 Oct 2023 12:30:27 +0200 Subject: [PATCH] add ability to edit a conversation title (#1921) * add ability to edit a conversation title * cleanup comments * insane div centering + mutate * use dark icons --- .../conversation/ConversationTitle.tsx | 122 +++++++++++++++++- 1 file changed, 117 insertions(+), 5 deletions(-) diff --git a/front/components/assistant/conversation/ConversationTitle.tsx b/front/components/assistant/conversation/ConversationTitle.tsx index da8ea14c9105..c206627011a6 100644 --- a/front/components/assistant/conversation/ConversationTitle.tsx +++ b/front/components/assistant/conversation/ConversationTitle.tsx @@ -2,12 +2,17 @@ import { ArrowUpOnSquareIcon, Avatar, Button, + CheckIcon, ClipboardCheckIcon, DropdownMenu, + IconButton, LinkStrokeIcon, + PencilSquareIcon, TrashIcon, + XMarkIcon, } from "@dust-tt/sparkle"; -import React, { useState } from "react"; +import React, { MouseEvent, useRef, useState } from "react"; +import { useSWRConfig } from "swr"; import { useConversation } from "@app/lib/swr"; import { WorkspaceType } from "@app/types/user"; @@ -23,7 +28,14 @@ export function ConversationTitle({ shareLink: string; onDelete?: () => void; }) { + const { mutate } = useSWRConfig(); + const [copyLinkSuccess, setCopyLinkSuccess] = useState(false); + const [isEditingTitle, setIsEditingTitle] = useState(true); + const [editedTitle, setEditedTitle] = useState(""); + + const titleInputFocused = useRef(false); + const saveButtonFocused = useRef(false); const handleClick = async () => { await navigator.clipboard.writeText(shareLink || ""); @@ -39,16 +51,116 @@ export function ConversationTitle({ workspaceId: owner.sId, }); + const onTitleChange = async (title: string) => { + try { + const res = await fetch( + `/api/w/${owner.sId}/assistant/conversations/${conversationId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + title, + visibility: conversation?.visibility, + }), + } + ); + await mutate( + `/api/w/${owner.sId}/assistant/conversations/${conversationId}` + ); + void mutate(`/api/w/${owner.sId}/assistant/conversations`); + if (!res.ok) { + throw new Error("Failed to update title"); + } + setIsEditingTitle(false); + setEditedTitle(""); + } catch (e) { + alert("Failed to update title"); + } + }; + if (isConversationLoading || isConversationError || !conversation) { return null; } return ( -
-
- {conversation?.title || ""} -
+
+
+ {!isEditingTitle ? ( +
+ {conversation?.title || ""} +
+ ) : ( +
+ setEditedTitle(e.target.value)} + // We need to make sure the click on the save button below + // is registered before the onBlur event, so we keep track of the + // focus state of both the input and the save button. + onFocus={() => (titleInputFocused.current = true)} + onBlur={() => { + setTimeout(() => { + if (!saveButtonFocused.current) { + setIsEditingTitle(false); + } + titleInputFocused.current = false; + }, 0); + }} + onKeyDown={(e) => { + if (e.key === "Enter") { + return onTitleChange(editedTitle); + } + }} + autoFocus + /> +
+ )} + {isEditingTitle ? ( +
+
) => { + e.preventDefault(); + return onTitleChange(editedTitle); + }} + // See comment on the input above. + onFocus={() => (saveButtonFocused.current = true)} + onBlur={() => { + setTimeout(() => { + if (!titleInputFocused.current) { + setIsEditingTitle(false); + } + saveButtonFocused.current = false; + }, 0); + }} + className="flex items-center" + > + +
+ { + setIsEditingTitle(false); + setEditedTitle(""); + }} + variant="secondary" + /> +
+ ) : ( + { + setEditedTitle(conversation?.title || ""); + setIsEditingTitle(true); + }} + size="sm" + variant="secondary" + /> + )} +