From c99b94dbe9bcee0014bad8111bd1ad705eab08a5 Mon Sep 17 00:00:00 2001 From: PierrunoYT <95778421+PierrunoYT@users.noreply.github.com> Date: Tue, 25 Jun 2024 08:15:44 +0200 Subject: [PATCH] feat(frontend): Add "Copy" Button to Chat Messages (#2619) * Add "Copy" Button to Chat Messages ### PR Overview: Add "Copy" Button to Chat Messages **Description:** This PR introduces a "Copy" button to each chat message in the `ChatMessage` component. The button allows users to easily copy the content of a chat message to their clipboard. The implementation includes a button with a clipboard icon and the necessary functionality to copy the message content. **Changes Made:** 1. **Imports:** - Added `FaClipboard` from `react-icons/fa` for the clipboard icon. - Added `toast` from `#utils/toast` for displaying notifications. 2. **New Functionality:** - Implemented `copyToClipboard` function using `navigator.clipboard.writeText` to copy the message content. - Added a button element with an `onClick` handler to trigger the `copyToClipboard` function. 3. **UI Enhancements:** - The button is styled to match the existing UI and is placed next to the message content. **Code Changes:** - Modified `frontend/src/components/chat/ChatMessage.tsx` to include the new button and functionality. **Testing:** - Verified that clicking the "Copy" button copies the message content to the clipboard. - Confirmed that a toast notification appears upon successful copy or failure. **Example Code:** ```tsx import React from "react"; import Markdown from "react-markdown"; import { twMerge } from "tailwind-merge"; import { code } from "../markdown/code"; import { FaClipboard } from "react-icons/fa"; import toast from "#/utils/toast"; // Assuming you have a toast utility for notifications interface MessageProps { message: Message; } function ChatMessage({ message }: MessageProps) { const className = twMerge( "markdown-body", "p-3 text-white max-w-[90%] overflow-y-auto rounded-lg", message.sender === "user" ? "bg-neutral-700 self-end" : "bg-neutral-500", ); const copyToClipboard = () => { navigator.clipboard.writeText(message.content).then(() => { toast.info("Message copied to clipboard!"); }).catch((error) => { toast.error(`Failed to copy message: ${error}`); }); }; return (
{message.content}
); } export default ChatMessage; ``` **Notes:** - Ensure that the `react-icons` package is installed (`npm install react-icons` or `yarn add react-icons`). - The toast utility is assumed to be available for notifications. If not, consider using an alternative notification method. * layout enhancements; linting --------- Co-authored-by: tobitege Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com> --- frontend/src/components/chat/ChatMessage.tsx | 36 +++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/chat/ChatMessage.tsx b/frontend/src/components/chat/ChatMessage.tsx index e0e51bbc37..095b08c6ca 100644 --- a/frontend/src/components/chat/ChatMessage.tsx +++ b/frontend/src/components/chat/ChatMessage.tsx @@ -1,23 +1,51 @@ -import React from "react"; +import React, { useState } from "react"; import Markdown from "react-markdown"; +import { FaClipboard } from "react-icons/fa"; import { twMerge } from "tailwind-merge"; import { code } from "../markdown/code"; +import toast from "#/utils/toast"; interface MessageProps { message: Message; } function ChatMessage({ message }: MessageProps) { - // const text = useTyping(message.content); + const [isHovering, setIsHovering] = useState(false); const className = twMerge( "markdown-body", - "p-3 text-white max-w-[90%] overflow-y-auto rounded-lg", + "p-3 text-white max-w-[90%] overflow-y-auto rounded-lg relative", message.sender === "user" ? "bg-neutral-700 self-end" : "bg-neutral-500", ); + const copyToClipboard = () => { + navigator.clipboard + .writeText(message.content) + .then(() => { + toast.info("Message copied to clipboard!"); + }) + .catch((error) => { + toast.error("copy-error", `Failed to copy message: ${error}`); + }); + }; + return ( -
+
setIsHovering(true)} + onMouseLeave={() => setIsHovering(false)} + > + {isHovering && ( + + )} {message.content}
);