diff --git a/apps/web/core/components/comments/card/root.tsx b/apps/web/core/components/comments/card/root.tsx index f19f9920b4c..2f86df1350c 100644 --- a/apps/web/core/components/comments/card/root.tsx +++ b/apps/web/core/components/comments/card/root.tsx @@ -1,7 +1,7 @@ -import type { FC } from "react"; -import { useRef, useState } from "react"; +import { useCallback, useRef, useState } from "react"; import { observer } from "mobx-react"; // plane imports +import { EmojiReactionButton, EmojiReactionPicker } from "@plane/propel/emoji-reaction"; import type { EditorRefApi } from "@plane/editor"; import type { TIssueComment, TCommentsOperations } from "@plane/types"; // plane web imports @@ -35,11 +35,23 @@ export const CommentCard = observer(function CommentCard(props: TCommentCard) { } = props; // states const [isEditing, setIsEditing] = useState(false); + const [isPickerOpen, setIsPickerOpen] = useState(false); // refs const readOnlyEditorRef = useRef(null); // derived values const workspaceId = comment?.workspace; + const userReactions = comment?.id ? activityOperations.userReactions(comment.id) : undefined; + + const handleEmojiSelect = useCallback( + (emoji: string) => { + if (!userReactions || !comment?.id) return; + // emoji is already in decimal string format from EmojiReactionPicker + void activityOperations.react(comment.id, emoji, userReactions); + }, + [activityOperations, comment?.id, userReactions] + ); + if (!comment || !workspaceId) return null; return ( @@ -47,13 +59,24 @@ export const CommentCard = observer(function CommentCard(props: TCommentCard) { comment={comment} quickActions={ !disabled && ( - setIsEditing(true)} - showAccessSpecifier={showAccessSpecifier} - showCopyLinkOption={showCopyLinkOption} - /> +
+ setIsPickerOpen(true)} />} + placement="bottom-start" + /> + + setIsEditing(true)} + showAccessSpecifier={showAccessSpecifier} + showCopyLinkOption={showCopyLinkOption} + /> +
) } ends={ends} diff --git a/apps/web/core/components/comments/comment-reaction.tsx b/apps/web/core/components/comments/comment-reaction.tsx index be93bc22c43..deb32bc92bb 100644 --- a/apps/web/core/components/comments/comment-reaction.tsx +++ b/apps/web/core/components/comments/comment-reaction.tsx @@ -61,6 +61,12 @@ export const CommentReactions = observer(function CommentReactions(props: TProps if (!userReactions) return null; + // Don't render anything if there are no reactions and it's disabled + if (reactions.length === 0 && disabled) return null; + + // Don't show the add button if there are no reactions + const showAddButton = !disabled && reactions.length > 0; + return (
setIsPickerOpen(true)} /> } diff --git a/apps/web/core/components/comments/quick-actions.tsx b/apps/web/core/components/comments/quick-actions.tsx index efb2ab2e61f..60af778a8f9 100644 --- a/apps/web/core/components/comments/quick-actions.tsx +++ b/apps/web/core/components/comments/quick-actions.tsx @@ -1,9 +1,10 @@ import { useMemo } from "react"; import { observer } from "mobx-react"; -import { Globe2, Link, Lock, Pencil, Trash2 } from "lucide-react"; +import { Globe2, Link, Lock, Pencil, Trash2, MoreHorizontal } from "lucide-react"; // plane imports import { EIssueCommentAccessSpecifier } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; +import { IconButton } from "@plane/propel/icon-button"; import type { TIssueComment, TCommentsOperations } from "@plane/types"; import type { TContextMenuItem } from "@plane/ui"; import { CustomMenu } from "@plane/ui"; @@ -76,7 +77,7 @@ export const CommentQuickActions = observer(function CommentQuickActions(props: ); return ( - + } closeOnSelect> {MENU_ITEMS.map((item) => { if (item.shouldRender === false) return null; diff --git a/apps/web/core/components/common/layout/sidebar/property-list-item.tsx b/apps/web/core/components/common/layout/sidebar/property-list-item.tsx index 491c74e6c35..4fd4ed0d1c1 100644 --- a/apps/web/core/components/common/layout/sidebar/property-list-item.tsx +++ b/apps/web/core/components/common/layout/sidebar/property-list-item.tsx @@ -13,7 +13,7 @@ export function SidebarPropertyListItem(props: TSidebarPropertyListItemProps) { const { icon: Icon, label, children, appendElement, childrenClassName } = props; return ( -
+
{label} diff --git a/packages/propel/src/emoji-reaction/emoji-reaction.tsx b/packages/propel/src/emoji-reaction/emoji-reaction.tsx index a3669da762d..802d6150b43 100644 --- a/packages/propel/src/emoji-reaction/emoji-reaction.tsx +++ b/packages/propel/src/emoji-reaction/emoji-reaction.tsx @@ -4,6 +4,7 @@ import { stringToEmoji } from "../emoji-icon-picker"; import { AddReactionIcon } from "../icons"; import { Tooltip } from "../tooltip"; import { cn } from "../utils"; +import { IconButton } from "../icon-button"; export interface EmojiReactionType { emoji: string; @@ -100,22 +101,17 @@ const EmojiReactionButton = React.forwardRef(function EmojiReactionButton( ref: React.ForwardedRef ) { return ( - + + + ); }); diff --git a/packages/propel/src/icons/arrows/index.ts b/packages/propel/src/icons/arrows/index.ts index eca5a8dee62..65513c4e20f 100644 --- a/packages/propel/src/icons/arrows/index.ts +++ b/packages/propel/src/icons/arrows/index.ts @@ -2,3 +2,4 @@ export * from "./chevron-down"; export * from "./chevron-left"; export * from "./chevron-right"; export * from "./chevron-up"; +export * from "./reply-icon"; diff --git a/packages/propel/src/icons/arrows/reply-icon.tsx b/packages/propel/src/icons/arrows/reply-icon.tsx new file mode 100644 index 00000000000..26daed5dd5e --- /dev/null +++ b/packages/propel/src/icons/arrows/reply-icon.tsx @@ -0,0 +1,13 @@ +import { IconWrapper } from "../icon-wrapper"; +import type { ISvgIcons } from "../type"; + +export function ReplyIcon({ color = "currentColor", ...rest }: ISvgIcons) { + return ( + + + + ); +} diff --git a/packages/propel/src/icons/constants.tsx b/packages/propel/src/icons/constants.tsx index 1b6778c6da7..e57721f199b 100644 --- a/packages/propel/src/icons/constants.tsx +++ b/packages/propel/src/icons/constants.tsx @@ -16,6 +16,7 @@ export const ArrowsIconsMap = [ { icon: , title: "ChevronLeftIcon" }, { icon: , title: "ChevronRightIcon" }, { icon: , title: "ChevronUpIcon" }, + { icon: , title: "ReplyIcon" }, ]; export const WorkspaceIconsMap = [ diff --git a/packages/propel/src/icons/registry.ts b/packages/propel/src/icons/registry.ts index cea01f88f98..715f80a201d 100644 --- a/packages/propel/src/icons/registry.ts +++ b/packages/propel/src/icons/registry.ts @@ -12,6 +12,7 @@ import { ChevronDownIcon } from "./arrows/chevron-down"; import { ChevronLeftIcon } from "./arrows/chevron-left"; import { ChevronRightIcon } from "./arrows/chevron-right"; import { ChevronUpIcon } from "./arrows/chevron-up"; +import { ReplyIcon } from "./arrows/reply-icon"; import { DefaultIcon } from "./default-icon"; import { BoardLayoutIcon } from "./layouts/board-icon"; import { CalendarLayoutIcon } from "./layouts/calendar-icon"; @@ -139,6 +140,7 @@ export const ICON_REGISTRY = { "arrow.chevron-left": ChevronLeftIcon, "arrow.chevron-right": ChevronRightIcon, "arrow.chevron-up": ChevronUpIcon, + "arrow.reply": ReplyIcon, // Default fallback default: DefaultIcon,