diff --git a/sparkle/src/components/ConversationMessage.tsx b/sparkle/src/components/ConversationMessage.tsx index 17bb85b58aac..a74569a80a5b 100644 --- a/sparkle/src/components/ConversationMessage.tsx +++ b/sparkle/src/components/ConversationMessage.tsx @@ -4,6 +4,7 @@ import { Button } from "@sparkle/components"; import { ConversationMessageActions, ConversationMessageEmojiSelectorProps, + ConversationMessageThumbSelectorProps, } from "@sparkle/components/ConversationMessageActions"; import { ConversationMessageContent } from "@sparkle/components/ConversationMessageContent"; import { ConversationMessageHeader } from "@sparkle/components/ConversationMessageHeader"; @@ -29,6 +30,7 @@ type ConversationMessageProps = { children?: React.ReactNode; citations?: React.ReactElement[]; messageEmoji?: ConversationMessageEmojiSelectorProps; + messageThumb?: ConversationMessageThumbSelectorProps; name: string | null; pictureUrl?: string | React.ReactNode | null; renderName?: (name: string | null) => React.ReactNode; @@ -46,6 +48,7 @@ export function ConversationMessage({ children, citations, messageEmoji, + messageThumb, name, pictureUrl, renderName = (name) => ( @@ -77,6 +80,7 @@ export function ConversationMessage({ ); diff --git a/sparkle/src/components/ConversationMessageActions.tsx b/sparkle/src/components/ConversationMessageActions.tsx index 3c1cbd59c123..31895691bb64 100644 --- a/sparkle/src/components/ConversationMessageActions.tsx +++ b/sparkle/src/components/ConversationMessageActions.tsx @@ -6,19 +6,42 @@ import { EmojiMartData, EmojiPicker, } from "@sparkle/components/EmojiPicker"; -import { Popover } from "@sparkle/components/Popover"; -import { ReactionIcon } from "@sparkle/icons/solid"; +import { Page } from "@sparkle/components/Page"; +import { + Popover, + PopoverContent, + PopoverRoot, + PopoverTrigger, +} from "@sparkle/components/Popover"; +import { TextArea } from "@sparkle/components/TextArea"; +import { + HandThumbDownIcon, + HandThumbUpIcon, + ReactionIcon, +} from "@sparkle/icons/solid"; import { cn } from "@sparkle/lib/utils"; type ConversationMessageActionsProps = { buttons?: React.ReactElement[]; messageEmoji?: ConversationMessageEmojiSelectorProps; + messageThumb?: ConversationMessageThumbSelectorProps; }; export function ConversationMessageActions({ buttons = [], messageEmoji, + messageThumb, }: ConversationMessageActionsProps) { + if (messageThumb) { + buttons.push( + + ); + } + if (messageEmoji) { buttons.push( Promise; + isSubmittingThumb: boolean; +} + function ConversationMessageEmojiSelector({ reactions, onSubmitEmoji, @@ -174,3 +207,104 @@ function EmojiSelector({ /> ); } + +function ConversationMessageThumbsSelector({ + onSubmitThumb, + isSubmittingThumb, +}: ConversationMessageThumbSelectorProps) { + return ( + + ); +} + +function ThumbsSelector({ + isSubmittingThumb = false, + onSubmitThumb, +}: ConversationMessageThumbSelectorProps) { + const [selectedThumb, setSelectedThumb] = + React.useState(null); + const [feedback, setFeedback] = React.useState(null); + const [isPopoverOpen, setIsPopoverOpen] = React.useState(false); + const containerRef = useRef(null); + + const selectThumb = async (thumb: ThumbReaction) => { + if (selectedThumb === thumb) { + setSelectedThumb(null); + setIsPopoverOpen(false); + await onSubmitThumb({ thumb, isToRemove: true }); + return; + } + setSelectedThumb(thumb); + setIsPopoverOpen(true); + await onSubmitThumb({ thumb, isToRemove: false }); + }; + + return ( + + + + + selectThumb("up")} + className={"s-rounded-r-none s-border-r-0"} + icon={HandThumbUpIcon} + /> + selectThumb("down")} + className={"s-rounded-l-none s-border-l-0"} + icon={HandThumbDownIcon} + /> + + + + + + {selectedThumb === "up" + ? "🎉 Glad you liked it! Tell us more?" + : "🫠Help make the answers better!"} + + setFeedback(e.target.value)} + /> + + { + await onSubmitThumb({ + thumb: selectedThumb ?? "up", + isToRemove: false, + feedback: feedback, + }); + setIsPopoverOpen(false); + }} + /> + setIsPopoverOpen(false)} + /> + + + + + + ); +} diff --git a/sparkle/src/stories/ConversationMessageActions.stories.tsx b/sparkle/src/stories/ConversationMessageActions.stories.tsx index 86b2209e1cda..d166c37368b4 100644 --- a/sparkle/src/stories/ConversationMessageActions.stories.tsx +++ b/sparkle/src/stories/ConversationMessageActions.stories.tsx @@ -1,7 +1,10 @@ import type { Meta, StoryObj } from "@storybook/react"; import React from "react"; -import { ConversationMessageEmojiSelectorProps } from "@sparkle/components/ConversationMessageActions"; +import { + ConversationMessageEmojiSelectorProps, + ConversationMessageThumbSelectorProps, +} from "@sparkle/components/ConversationMessageActions"; import { ArrowPathIcon, @@ -46,6 +49,13 @@ const messageEmoji: ConversationMessageEmojiSelectorProps = { isSubmittingEmoji: false, }; +const messageThumb: ConversationMessageThumbSelectorProps = { + onSubmitThumb: async (element) => { + console.log("Thumb clicked", element); + }, + isSubmittingThumb: false, +}; + const meta = { title: "Primitives/ConversationMessageActions", component: ConversationMessageActions, @@ -60,6 +70,12 @@ const meta = { type: "object", }, }, + messageThumb: { + description: "Whether to show the thumbs selector", + control: { + type: "object", + }, + }, }, } satisfies Meta>; @@ -70,7 +86,6 @@ export const ExamplePicker: Story = { args: { buttons, messageEmoji, + messageThumb, }, }; - -;