Skip to content

Commit df69886

Browse files
[WEB-5614] chore: work item detail comment and sidebar enhancements (#8397)
1 parent f3d5e7d commit df69886

File tree

9 files changed

+72
-29
lines changed

9 files changed

+72
-29
lines changed

apps/web/core/components/comments/card/root.tsx

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import type { FC } from "react";
2-
import { useRef, useState } from "react";
1+
import { useCallback, useRef, useState } from "react";
32
import { observer } from "mobx-react";
43
// plane imports
4+
import { EmojiReactionButton, EmojiReactionPicker } from "@plane/propel/emoji-reaction";
55
import type { EditorRefApi } from "@plane/editor";
66
import type { TIssueComment, TCommentsOperations } from "@plane/types";
77
// plane web imports
@@ -35,25 +35,48 @@ export const CommentCard = observer(function CommentCard(props: TCommentCard) {
3535
} = props;
3636
// states
3737
const [isEditing, setIsEditing] = useState(false);
38+
const [isPickerOpen, setIsPickerOpen] = useState(false);
3839
// refs
3940
const readOnlyEditorRef = useRef<EditorRefApi>(null);
4041
// derived values
4142
const workspaceId = comment?.workspace;
4243

44+
const userReactions = comment?.id ? activityOperations.userReactions(comment.id) : undefined;
45+
46+
const handleEmojiSelect = useCallback(
47+
(emoji: string) => {
48+
if (!userReactions || !comment?.id) return;
49+
// emoji is already in decimal string format from EmojiReactionPicker
50+
void activityOperations.react(comment.id, emoji, userReactions);
51+
},
52+
[activityOperations, comment?.id, userReactions]
53+
);
54+
4355
if (!comment || !workspaceId) return null;
4456

4557
return (
4658
<CommentBlock
4759
comment={comment}
4860
quickActions={
4961
!disabled && (
50-
<CommentQuickActions
51-
activityOperations={activityOperations}
52-
comment={comment}
53-
setEditMode={() => setIsEditing(true)}
54-
showAccessSpecifier={showAccessSpecifier}
55-
showCopyLinkOption={showCopyLinkOption}
56-
/>
62+
<div className="flex items-center gap-1">
63+
<EmojiReactionPicker
64+
isOpen={isPickerOpen}
65+
handleToggle={setIsPickerOpen}
66+
onChange={handleEmojiSelect}
67+
disabled={disabled}
68+
label={<EmojiReactionButton onAddReaction={() => setIsPickerOpen(true)} />}
69+
placement="bottom-start"
70+
/>
71+
72+
<CommentQuickActions
73+
activityOperations={activityOperations}
74+
comment={comment}
75+
setEditMode={() => setIsEditing(true)}
76+
showAccessSpecifier={showAccessSpecifier}
77+
showCopyLinkOption={showCopyLinkOption}
78+
/>
79+
</div>
5780
)
5881
}
5982
ends={ends}

apps/web/core/components/comments/comment-reaction.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ export const CommentReactions = observer(function CommentReactions(props: TProps
6161

6262
if (!userReactions) return null;
6363

64+
// Don't render anything if there are no reactions and it's disabled
65+
if (reactions.length === 0 && disabled) return null;
66+
67+
// Don't show the add button if there are no reactions
68+
const showAddButton = !disabled && reactions.length > 0;
69+
6470
return (
6571
<div className="relative">
6672
<EmojiReactionPicker
@@ -72,7 +78,7 @@ export const CommentReactions = observer(function CommentReactions(props: TProps
7278
<EmojiReactionGroup
7379
reactions={reactions}
7480
onReactionClick={handleReactionClick}
75-
showAddButton={!disabled}
81+
showAddButton={showAddButton}
7682
onAddReaction={() => setIsPickerOpen(true)}
7783
/>
7884
}

apps/web/core/components/comments/quick-actions.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { useMemo } from "react";
22
import { observer } from "mobx-react";
3-
import { Globe2, Link, Lock, Pencil, Trash2 } from "lucide-react";
3+
import { Globe2, Link, Lock, Pencil, Trash2, MoreHorizontal } from "lucide-react";
44
// plane imports
55
import { EIssueCommentAccessSpecifier } from "@plane/constants";
66
import { useTranslation } from "@plane/i18n";
7+
import { IconButton } from "@plane/propel/icon-button";
78
import type { TIssueComment, TCommentsOperations } from "@plane/types";
89
import type { TContextMenuItem } from "@plane/ui";
910
import { CustomMenu } from "@plane/ui";
@@ -76,7 +77,7 @@ export const CommentQuickActions = observer(function CommentQuickActions(props:
7677
);
7778

7879
return (
79-
<CustomMenu ellipsis closeOnSelect>
80+
<CustomMenu customButton={<IconButton icon={MoreHorizontal} variant="ghost" size="sm" />} closeOnSelect>
8081
{MENU_ITEMS.map((item) => {
8182
if (item.shouldRender === false) return null;
8283

apps/web/core/components/common/layout/sidebar/property-list-item.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export function SidebarPropertyListItem(props: TSidebarPropertyListItemProps) {
1313
const { icon: Icon, label, children, appendElement, childrenClassName } = props;
1414

1515
return (
16-
<div className="flex items-center gap-2">
16+
<div className="flex items-start gap-2">
1717
<div className="flex shrink-0 items-center gap-1.5 w-30 text-body-xs-regular text-tertiary h-7.5">
1818
<Icon className="size-4 shrink-0" />
1919
<span>{label}</span>

packages/propel/src/emoji-reaction/emoji-reaction.tsx

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { stringToEmoji } from "../emoji-icon-picker";
44
import { AddReactionIcon } from "../icons";
55
import { Tooltip } from "../tooltip";
66
import { cn } from "../utils";
7+
import { IconButton } from "../icon-button";
78

89
export interface EmojiReactionType {
910
emoji: string;
@@ -100,22 +101,17 @@ const EmojiReactionButton = React.forwardRef(function EmojiReactionButton(
100101
ref: React.ForwardedRef<HTMLButtonElement>
101102
) {
102103
return (
103-
<button
104-
ref={ref}
105-
onClick={onAddReaction}
106-
className={cn(
107-
"inline-flex items-center justify-center rounded-full border border-dashed border-strong",
108-
"bg-surface-1 text-placeholder transition-all duration-200",
109-
"hover:border-accent-strong hover:text-accent-primary hover:bg-accent-primary/5",
110-
"focus:outline-none focus:ring-2 focus:ring-accent-strong/20 focus:ring-offset-1",
111-
"h-6 w-6",
112-
className
113-
)}
114-
title="Add reaction"
115-
{...props}
116-
>
117-
<AddReactionIcon className="h-3 w-3" />
118-
</button>
104+
<Tooltip tooltipContent="Add reaction">
105+
<IconButton
106+
ref={ref}
107+
icon={AddReactionIcon}
108+
variant="ghost"
109+
size="sm"
110+
onClick={onAddReaction}
111+
className={className}
112+
{...props}
113+
/>
114+
</Tooltip>
119115
);
120116
});
121117

packages/propel/src/icons/arrows/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from "./chevron-down";
22
export * from "./chevron-left";
33
export * from "./chevron-right";
44
export * from "./chevron-up";
5+
export * from "./reply-icon";
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { IconWrapper } from "../icon-wrapper";
2+
import type { ISvgIcons } from "../type";
3+
4+
export function ReplyIcon({ color = "currentColor", ...rest }: ISvgIcons) {
5+
return (
6+
<IconWrapper color={color} clipPathId="clip0_2890_23" {...rest}>
7+
<path
8+
d="M5.52865 4.19526C5.789 3.93491 6.211 3.93491 6.47135 4.19526C6.73168 4.45561 6.7317 4.87763 6.47135 5.13797L4.27604 7.33328H10.6667C11.5507 7.33328 12.3983 7.68472 13.0234 8.30985C13.6485 8.93496 14 9.78257 14 10.6666V11.9999C14 12.3681 13.7015 12.6666 13.3333 12.6666C12.9652 12.6666 12.6667 12.3681 12.6667 11.9999V10.6666C12.6667 10.1362 12.4558 9.62762 12.0807 9.25255C11.7526 8.92442 11.3223 8.72189 10.8646 8.67638L10.6667 8.66662H4.27604L6.47135 10.8619C6.73168 11.1223 6.7317 11.5443 6.47135 11.8046C6.21101 12.065 5.78899 12.065 5.52865 11.8046L2.19531 8.4713C2.1642 8.4402 2.13648 8.40583 2.11198 8.36909C2.09464 8.3431 2.07971 8.31603 2.06641 8.28836C2.05178 8.25798 2.03859 8.22669 2.02865 8.19396C2.02526 8.18281 2.02297 8.17139 2.02018 8.16011C2.0075 8.10875 2 8.05523 2 7.99995C2 7.94683 2.00649 7.89518 2.01823 7.84565C2.02148 7.83195 2.02452 7.81815 2.02865 7.80464C2.03858 7.77214 2.05185 7.74107 2.06641 7.71089C2.07918 7.68436 2.09353 7.65841 2.11003 7.63341C2.13495 7.59564 2.16344 7.56047 2.19531 7.5286L5.52865 4.19526Z"
9+
fill={color}
10+
/>
11+
</IconWrapper>
12+
);
13+
}

packages/propel/src/icons/constants.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const ArrowsIconsMap = [
1616
{ icon: <Icon name="arrow.chevron-left" />, title: "ChevronLeftIcon" },
1717
{ icon: <Icon name="arrow.chevron-right" />, title: "ChevronRightIcon" },
1818
{ icon: <Icon name="arrow.chevron-up" />, title: "ChevronUpIcon" },
19+
{ icon: <Icon name="arrow.reply" />, title: "ReplyIcon" },
1920
];
2021

2122
export const WorkspaceIconsMap = [

packages/propel/src/icons/registry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { ChevronDownIcon } from "./arrows/chevron-down";
1212
import { ChevronLeftIcon } from "./arrows/chevron-left";
1313
import { ChevronRightIcon } from "./arrows/chevron-right";
1414
import { ChevronUpIcon } from "./arrows/chevron-up";
15+
import { ReplyIcon } from "./arrows/reply-icon";
1516
import { DefaultIcon } from "./default-icon";
1617
import { BoardLayoutIcon } from "./layouts/board-icon";
1718
import { CalendarLayoutIcon } from "./layouts/calendar-icon";
@@ -139,6 +140,7 @@ export const ICON_REGISTRY = {
139140
"arrow.chevron-left": ChevronLeftIcon,
140141
"arrow.chevron-right": ChevronRightIcon,
141142
"arrow.chevron-up": ChevronUpIcon,
143+
"arrow.reply": ReplyIcon,
142144

143145
// Default fallback
144146
default: DefaultIcon,

0 commit comments

Comments
 (0)