mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 08:46:22 +02:00
feat: improve chat renaming functionality with dialog support in sidebar components
This commit is contained in:
parent
f3652ad7cf
commit
f0d170a595
12 changed files with 213 additions and 23 deletions
|
|
@ -6,6 +6,7 @@ import {
|
|||
ArchiveIcon,
|
||||
MessageCircleMore,
|
||||
MoreHorizontal,
|
||||
PenLine,
|
||||
RotateCcwIcon,
|
||||
Search,
|
||||
Trash2,
|
||||
|
|
@ -17,6 +18,14 @@ import { useTranslations } from "next-intl";
|
|||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -69,6 +78,10 @@ export function AllPrivateChatsSidebar({
|
|||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [showArchived, setShowArchived] = useState(false);
|
||||
const [openDropdownId, setOpenDropdownId] = useState<number | null>(null);
|
||||
const [showRenameDialog, setShowRenameDialog] = useState(false);
|
||||
const [renamingThread, setRenamingThread] = useState<{ id: number; title: string } | null>(null);
|
||||
const [newTitle, setNewTitle] = useState("");
|
||||
const [isRenaming, setIsRenaming] = useState(false);
|
||||
const debouncedSearchQuery = useDebouncedValue(searchQuery, 300);
|
||||
|
||||
const isSearchMode = !!debouncedSearchQuery.trim();
|
||||
|
|
@ -187,6 +200,35 @@ export function AllPrivateChatsSidebar({
|
|||
[queryClient, searchSpaceId, t]
|
||||
);
|
||||
|
||||
const handleStartRename = useCallback((threadId: number, title: string) => {
|
||||
setRenamingThread({ id: threadId, title });
|
||||
setNewTitle(title);
|
||||
setShowRenameDialog(true);
|
||||
}, []);
|
||||
|
||||
const handleConfirmRename = useCallback(async () => {
|
||||
if (!renamingThread || !newTitle.trim()) return;
|
||||
setIsRenaming(true);
|
||||
try {
|
||||
await updateThread(renamingThread.id, { title: newTitle.trim() });
|
||||
toast.success(t("chat_renamed") || "Chat renamed");
|
||||
queryClient.invalidateQueries({ queryKey: ["all-threads", searchSpaceId] });
|
||||
queryClient.invalidateQueries({ queryKey: ["search-threads", searchSpaceId] });
|
||||
queryClient.invalidateQueries({ queryKey: ["threads", searchSpaceId] });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["threads", searchSpaceId, "detail", String(renamingThread.id)],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error renaming thread:", error);
|
||||
toast.error(t("error_renaming_chat") || "Failed to rename chat");
|
||||
} finally {
|
||||
setIsRenaming(false);
|
||||
setShowRenameDialog(false);
|
||||
setRenamingThread(null);
|
||||
setNewTitle("");
|
||||
}
|
||||
}, [renamingThread, newTitle, queryClient, searchSpaceId, t]);
|
||||
|
||||
const handleClearSearch = useCallback(() => {
|
||||
setSearchQuery("");
|
||||
}, []);
|
||||
|
|
@ -355,11 +397,19 @@ export function AllPrivateChatsSidebar({
|
|||
<span className="sr-only">{t("more_options") || "More options"}</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-40 z-80">
|
||||
<DropdownMenuContent align="end" className="w-40 z-80">
|
||||
{!thread.archived && (
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleToggleArchive(thread.id, thread.archived)}
|
||||
disabled={isArchiving}
|
||||
onClick={() => handleStartRename(thread.id, thread.title || "New Chat")}
|
||||
>
|
||||
<PenLine className="mr-2 h-4 w-4" />
|
||||
<span>{t("rename") || "Rename"}</span>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleToggleArchive(thread.id, thread.archived)}
|
||||
disabled={isArchiving}
|
||||
>
|
||||
{thread.archived ? (
|
||||
<>
|
||||
<RotateCcwIcon className="mr-2 h-4 w-4" />
|
||||
|
|
@ -412,6 +462,51 @@ export function AllPrivateChatsSidebar({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Dialog open={showRenameDialog} onOpenChange={setShowRenameDialog}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<span>{t("rename_chat") || "Rename Chat"}</span>
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{t("rename_chat_description") || "Enter a new name for this conversation."}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Input
|
||||
value={newTitle}
|
||||
onChange={(e) => setNewTitle(e.target.value)}
|
||||
placeholder={t("chat_title_placeholder") || "Chat title"}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" && !isRenaming && newTitle.trim()) {
|
||||
handleConfirmRename();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<DialogFooter className="flex gap-2 sm:justify-end">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setShowRenameDialog(false)}
|
||||
disabled={isRenaming}
|
||||
>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleConfirmRename}
|
||||
disabled={isRenaming || !newTitle.trim()}
|
||||
className="gap-2"
|
||||
>
|
||||
{isRenaming ? (
|
||||
<>
|
||||
<Spinner size="xs" />
|
||||
<span>{t("renaming") || "Renaming"}</span>
|
||||
</>
|
||||
) : (
|
||||
<span>{t("rename") || "Rename"}</span>
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</SidebarSlideOutPanel>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue