[WEB-720] chore: reaction tooltip added (#3945)

* chore: reaction tooltip added

* chore: reaction tooltip updated

* chore: issue reaction tooltip updated

* chore: helper function updated
This commit is contained in:
Anmol Singh Bhatia 2024-03-12 19:12:25 +05:30 committed by GitHub
parent c97b994311
commit 8aca74c68d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 85 additions and 35 deletions

View File

@ -1,10 +1,11 @@
import { FC, useMemo } from "react";
import { observer } from "mobx-react-lite";
// components
import { TOAST_TYPE, setToast } from "@plane/ui";
import { TOAST_TYPE, Tooltip, setToast } from "@plane/ui";
import { renderEmoji } from "helpers/emoji.helper";
import { useIssueDetail } from "hooks/store";
// ui
import { useIssueDetail, useMember } from "hooks/store";
// helper
import { formatTextList } from "helpers/issue.helper";
// types
import { IUser } from "@plane/types";
import { ReactionSelector } from "./reaction-selector";
@ -21,10 +22,11 @@ export const IssueCommentReaction: FC<TIssueCommentReaction> = observer((props)
// hooks
const {
commentReaction: { getCommentReactionsByCommentId, commentReactionsByUser },
commentReaction: { getCommentReactionsByCommentId, commentReactionsByUser, getCommentReactionById },
createCommentReaction,
removeCommentReaction,
} = useIssueDetail();
const { getUserDetails } = useMember();
const reactionIds = getCommentReactionsByCommentId(commentId);
const userReactions = commentReactionsByUser(commentId, currentUser.id).map((r) => r.reaction);
@ -73,6 +75,17 @@ export const IssueCommentReaction: FC<TIssueCommentReaction> = observer((props)
[workspaceSlug, projectId, commentId, currentUser, createCommentReaction, removeCommentReaction, userReactions]
);
const getReactionUsers = (reaction: string): string => {
const reactionUsers = (reactionIds?.[reaction] || [])
.map((reactionId) => {
const reactionDetails = getCommentReactionById(reactionId);
return reactionDetails ? getUserDetails(reactionDetails.actor)?.display_name : null;
})
.filter((displayName): displayName is string => !!displayName);
const formattedUsers = formatTextList(reactionUsers);
return formattedUsers;
};
return (
<div className="mt-4 relative flex items-center gap-1.5">
<ReactionSelector
@ -87,19 +100,21 @@ export const IssueCommentReaction: FC<TIssueCommentReaction> = observer((props)
(reaction) =>
reactionIds[reaction]?.length > 0 && (
<>
<button
type="button"
onClick={() => issueCommentReactionOperations.react(reaction)}
key={reaction}
className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${
userReactions.includes(reaction) ? "bg-custom-primary-100/10" : "bg-custom-background-80"
}`}
>
<span>{renderEmoji(reaction)}</span>
<span className={userReactions.includes(reaction) ? "text-custom-primary-100" : ""}>
{(reactionIds || {})[reaction].length}{" "}
</span>
</button>
<Tooltip tooltipContent={getReactionUsers(reaction)}>
<button
type="button"
onClick={() => issueCommentReactionOperations.react(reaction)}
key={reaction}
className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${
userReactions.includes(reaction) ? "bg-custom-primary-100/10" : "bg-custom-background-80"
}`}
>
<span>{renderEmoji(reaction)}</span>
<span className={userReactions.includes(reaction) ? "text-custom-primary-100" : ""}>
{(reactionIds || {})[reaction].length}{" "}
</span>
</button>
</Tooltip>
</>
)
)}

View File

@ -1,10 +1,12 @@
import { FC, useMemo } from "react";
import { observer } from "mobx-react-lite";
// components
import { TOAST_TYPE, setToast } from "@plane/ui";
import { renderEmoji } from "helpers/emoji.helper";
import { useIssueDetail } from "hooks/store";
// hooks
import { useIssueDetail, useMember } from "hooks/store";
// ui
import { TOAST_TYPE, Tooltip, setToast } from "@plane/ui";
// helpers
import { renderEmoji } from "helpers/emoji.helper";
import { formatTextList } from "helpers/issue.helper";
// types
import { IUser } from "@plane/types";
import { ReactionSelector } from "./reaction-selector";
@ -20,10 +22,11 @@ export const IssueReaction: FC<TIssueReaction> = observer((props) => {
const { workspaceSlug, projectId, issueId, currentUser } = props;
// hooks
const {
reaction: { getReactionsByIssueId, reactionsByUser },
reaction: { getReactionsByIssueId, reactionsByUser, getReactionById },
createReaction,
removeReaction,
} = useIssueDetail();
const { getUserDetails } = useMember();
const reactionIds = getReactionsByIssueId(issueId);
const userReactions = reactionsByUser(issueId, currentUser.id).map((r) => r.reaction);
@ -72,6 +75,18 @@ export const IssueReaction: FC<TIssueReaction> = observer((props) => {
[workspaceSlug, projectId, issueId, currentUser, createReaction, removeReaction, userReactions]
);
const getReactionUsers = (reaction: string): string => {
const reactionUsers = (reactionIds?.[reaction] || [])
.map((reactionId) => {
const reactionDetails = getReactionById(reactionId);
return reactionDetails ? getUserDetails(reactionDetails.actor_id)?.display_name : null;
})
.filter((displayName): displayName is string => !!displayName);
const formattedUsers = formatTextList(reactionUsers);
return formattedUsers;
};
return (
<div className="mt-4 relative flex items-center gap-1.5">
<ReactionSelector size="md" position="top" value={userReactions} onSelect={issueReactionOperations.react} />
@ -81,19 +96,21 @@ export const IssueReaction: FC<TIssueReaction> = observer((props) => {
(reaction) =>
reactionIds[reaction]?.length > 0 && (
<>
<button
type="button"
onClick={() => issueReactionOperations.react(reaction)}
key={reaction}
className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${
userReactions.includes(reaction) ? "bg-custom-primary-100/10" : "bg-custom-background-80"
}`}
>
<span>{renderEmoji(reaction)}</span>
<span className={userReactions.includes(reaction) ? "text-custom-primary-100" : ""}>
{(reactionIds || {})[reaction].length}{" "}
</span>
</button>
<Tooltip tooltipContent={getReactionUsers(reaction)}>
<button
type="button"
onClick={() => issueReactionOperations.react(reaction)}
key={reaction}
className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${
userReactions.includes(reaction) ? "bg-custom-primary-100/10" : "bg-custom-background-80"
}`}
>
<span>{renderEmoji(reaction)}</span>
<span className={userReactions.includes(reaction) ? "text-custom-primary-100" : ""}>
{(reactionIds || {})[reaction].length}{" "}
</span>
</button>
</Tooltip>
</>
)
)}

View File

@ -184,3 +184,21 @@ export function getChangedIssuefields(formData: Partial<TIssue>, dirtyFields: {
return changedFields;
}
export const formatTextList = (TextArray: string[]): string => {
const count = TextArray.length;
switch (count) {
case 0:
return "";
case 1:
return TextArray[0];
case 2:
return `${TextArray[0]} and ${TextArray[1]}`;
case 3:
return `${TextArray.slice(0, 2).join(", ")}, and ${TextArray[2]}`;
case 4:
return `${TextArray.slice(0, 3).join(", ")}, and ${TextArray[3]}`;
default:
return `${TextArray.slice(0, 3).join(", ")}, and +${count - 3} more`;
}
};