Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Allow users to edit chat thread title #6

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts

# secrets
start.sh
Comment on lines +33 to +35

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove

2 changes: 1 addition & 1 deletion src/components/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const Menu = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("w-64 flex flex-col", className)} {...props} />
<div ref={ref} className={cn("w-96 flex flex-col", className)} {...props} />
));

Menu.displayName = "Menu";
Expand Down
23 changes: 18 additions & 5 deletions src/features/chat/chat-menu/chat-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,43 @@ interface Prop {
export const ChatMenu = (prop:Prop) => {
const [items, setItems] = useState<ChatThreadModel[]>([]);

// Fetch data when the component mounts
const [refresh, setRefresh] = useState(true);
// Fetch data when the component mounts and refreshes
useEffect(() => {
const fetchData = async () => {
console.log("fetching items")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove

const data = await FindAllChatThreadForCurrentUser();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this function handle fetching errors?

setItems(data);
};

fetchData();
}, []);
}, [refresh]);

const doRefresh = () => {
console.log("should refresh")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

setRefresh(!refresh);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
setRefresh(!refresh);
setRefresh(prev => !prev)

}

return (
<div>
<Button size={"icon"} onClick={() => prop.setIsOpen(!prop.isOpen)} >
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" width="12" height="12" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
{prop.isOpen ?
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" width="12" height="12" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 4v16M12 4v16M18 4v16" />
</svg>
:
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" width="12" height="12" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
}
</Button>
{prop.isOpen ?
<Menu>
<MenuHeader className="justify-end">
<NewChat />
</MenuHeader>
<MenuContent>
<MenuItems menuItems={items} />
<MenuItems refresh={doRefresh} menuItems={items} />
</MenuContent>
<MenuFooter>
<div className="flex flex-col gap-3">
Expand Down
28 changes: 24 additions & 4 deletions src/features/chat/chat-menu/menu-items.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
"use client";
import { MenuItem } from "@/components/menu";
import { Button } from "@/components/ui/button";
import { SoftDeleteChatThreadByID } from "@/features/chat/chat-services/chat-thread-service";
import { FileText, MessageCircle, Trash } from "lucide-react";
import { SoftDeleteChatThreadByID, userUpdateThreadTitle } from "@/features/chat/chat-services/chat-thread-service";
import { FileText, MessageCircle, Trash, PenSquare, Navigation } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { FC } from "react";
import { ChatThreadModel } from "../chat-services/models";

interface Prop {
interface RProp {
menuItems: Array<ChatThreadModel>;
refresh: () => void
}

export const MenuItems: FC<Prop> = (props) => {
export const MenuItems: FC<RProp> = (props) => {
const { id } = useParams();
const router = useRouter();

Expand All @@ -21,6 +22,11 @@ export const MenuItems: FC<Prop> = (props) => {
router.replace("/chat");
};

const renameTitle = async (threadID: string, title: string) => {
await userUpdateThreadTitle(threadID, title);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if this fails?

props.refresh()
};

return (
<>
{props.menuItems.map((thread) => (
Expand All @@ -45,6 +51,20 @@ export const MenuItems: FC<Prop> = (props) => {
<span className="flex gap-2 items-center overflow-hidden flex-1">
<span className="overflow-ellipsis truncate"> {thread.name}</span>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're truncating thread names. If thread names are longer than 30 characters you will want to render ellipses here perhaps (...)

</span>
<Button
className="invisible group-hover/item:visible"
size={"sm"}
variant={"ghost"}
onClick={async (e) => {
e.preventDefault();
const threadTitle = prompt("Enter the thread title", thread.name);
if (threadTitle) {
await renameTitle(thread.id, threadTitle);
}
}}
>
<PenSquare size={16} />
</Button>
<Button
className="invisible group-hover/item:visible hover:text-brand"
size={"sm"}
Expand Down
17 changes: 17 additions & 0 deletions src/features/chat/chat-services/chat-thread-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,23 @@ export const UpsertChatThread = async (chatThread: ChatThreadModel) => {
return updatedChatThread;
};

export const userUpdateThreadTitle = async (
chatThreadID: string,
userTitle: string
) => {

const threads = await FindChatThreadByID(chatThreadID);

if (threads.length === 1) {
const chatThread = threads[0]
await UpsertChatThread({
...chatThread,
name: userTitle.substring(0, 30),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of truncating here, prevent user from putting down more than 30 characters. That way we don't store truncated names in database. i.e. Review legal documents around tax fil

});
}

};

export const updateChatThreadTitle = async (
chatThread: ChatThreadModel,
messages: ChatMessageModel[],
Expand Down
2 changes: 1 addition & 1 deletion src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"eslint": "^8.46.0",
"eslint-config-next": "^13.4.12",
"langchain": "^0.0.123",
"lucide-react": "^0.264.0",
"lucide-react": "^0.279.0",
"nanoid": "^4.0.2",
"next": "^13.4.12",
"next-auth": "^4.22.4",
Expand Down