forked from github/plane
[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:
parent
c97b994311
commit
8aca74c68d
@ -1,10 +1,11 @@
|
|||||||
import { FC, useMemo } from "react";
|
import { FC, useMemo } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// components
|
// components
|
||||||
import { TOAST_TYPE, setToast } from "@plane/ui";
|
import { TOAST_TYPE, Tooltip, setToast } from "@plane/ui";
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
import { useIssueDetail } from "hooks/store";
|
import { useIssueDetail, useMember } from "hooks/store";
|
||||||
// ui
|
// helper
|
||||||
|
import { formatTextList } from "helpers/issue.helper";
|
||||||
// types
|
// types
|
||||||
import { IUser } from "@plane/types";
|
import { IUser } from "@plane/types";
|
||||||
import { ReactionSelector } from "./reaction-selector";
|
import { ReactionSelector } from "./reaction-selector";
|
||||||
@ -21,10 +22,11 @@ export const IssueCommentReaction: FC<TIssueCommentReaction> = observer((props)
|
|||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
const {
|
const {
|
||||||
commentReaction: { getCommentReactionsByCommentId, commentReactionsByUser },
|
commentReaction: { getCommentReactionsByCommentId, commentReactionsByUser, getCommentReactionById },
|
||||||
createCommentReaction,
|
createCommentReaction,
|
||||||
removeCommentReaction,
|
removeCommentReaction,
|
||||||
} = useIssueDetail();
|
} = useIssueDetail();
|
||||||
|
const { getUserDetails } = useMember();
|
||||||
|
|
||||||
const reactionIds = getCommentReactionsByCommentId(commentId);
|
const reactionIds = getCommentReactionsByCommentId(commentId);
|
||||||
const userReactions = commentReactionsByUser(commentId, currentUser.id).map((r) => r.reaction);
|
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]
|
[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 (
|
return (
|
||||||
<div className="mt-4 relative flex items-center gap-1.5">
|
<div className="mt-4 relative flex items-center gap-1.5">
|
||||||
<ReactionSelector
|
<ReactionSelector
|
||||||
@ -87,19 +100,21 @@ export const IssueCommentReaction: FC<TIssueCommentReaction> = observer((props)
|
|||||||
(reaction) =>
|
(reaction) =>
|
||||||
reactionIds[reaction]?.length > 0 && (
|
reactionIds[reaction]?.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<button
|
<Tooltip tooltipContent={getReactionUsers(reaction)}>
|
||||||
type="button"
|
<button
|
||||||
onClick={() => issueCommentReactionOperations.react(reaction)}
|
type="button"
|
||||||
key={reaction}
|
onClick={() => issueCommentReactionOperations.react(reaction)}
|
||||||
className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${
|
key={reaction}
|
||||||
userReactions.includes(reaction) ? "bg-custom-primary-100/10" : "bg-custom-background-80"
|
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" : ""}>
|
<span>{renderEmoji(reaction)}</span>
|
||||||
{(reactionIds || {})[reaction].length}{" "}
|
<span className={userReactions.includes(reaction) ? "text-custom-primary-100" : ""}>
|
||||||
</span>
|
{(reactionIds || {})[reaction].length}{" "}
|
||||||
</button>
|
</span>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { FC, useMemo } from "react";
|
import { FC, useMemo } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// components
|
// hooks
|
||||||
import { TOAST_TYPE, setToast } from "@plane/ui";
|
import { useIssueDetail, useMember } from "hooks/store";
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
|
||||||
import { useIssueDetail } from "hooks/store";
|
|
||||||
// ui
|
// ui
|
||||||
|
import { TOAST_TYPE, Tooltip, setToast } from "@plane/ui";
|
||||||
|
// helpers
|
||||||
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
import { formatTextList } from "helpers/issue.helper";
|
||||||
// types
|
// types
|
||||||
import { IUser } from "@plane/types";
|
import { IUser } from "@plane/types";
|
||||||
import { ReactionSelector } from "./reaction-selector";
|
import { ReactionSelector } from "./reaction-selector";
|
||||||
@ -20,10 +22,11 @@ export const IssueReaction: FC<TIssueReaction> = observer((props) => {
|
|||||||
const { workspaceSlug, projectId, issueId, currentUser } = props;
|
const { workspaceSlug, projectId, issueId, currentUser } = props;
|
||||||
// hooks
|
// hooks
|
||||||
const {
|
const {
|
||||||
reaction: { getReactionsByIssueId, reactionsByUser },
|
reaction: { getReactionsByIssueId, reactionsByUser, getReactionById },
|
||||||
createReaction,
|
createReaction,
|
||||||
removeReaction,
|
removeReaction,
|
||||||
} = useIssueDetail();
|
} = useIssueDetail();
|
||||||
|
const { getUserDetails } = useMember();
|
||||||
|
|
||||||
const reactionIds = getReactionsByIssueId(issueId);
|
const reactionIds = getReactionsByIssueId(issueId);
|
||||||
const userReactions = reactionsByUser(issueId, currentUser.id).map((r) => r.reaction);
|
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]
|
[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 (
|
return (
|
||||||
<div className="mt-4 relative flex items-center gap-1.5">
|
<div className="mt-4 relative flex items-center gap-1.5">
|
||||||
<ReactionSelector size="md" position="top" value={userReactions} onSelect={issueReactionOperations.react} />
|
<ReactionSelector size="md" position="top" value={userReactions} onSelect={issueReactionOperations.react} />
|
||||||
@ -81,19 +96,21 @@ export const IssueReaction: FC<TIssueReaction> = observer((props) => {
|
|||||||
(reaction) =>
|
(reaction) =>
|
||||||
reactionIds[reaction]?.length > 0 && (
|
reactionIds[reaction]?.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<button
|
<Tooltip tooltipContent={getReactionUsers(reaction)}>
|
||||||
type="button"
|
<button
|
||||||
onClick={() => issueReactionOperations.react(reaction)}
|
type="button"
|
||||||
key={reaction}
|
onClick={() => issueReactionOperations.react(reaction)}
|
||||||
className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${
|
key={reaction}
|
||||||
userReactions.includes(reaction) ? "bg-custom-primary-100/10" : "bg-custom-background-80"
|
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" : ""}>
|
<span>{renderEmoji(reaction)}</span>
|
||||||
{(reactionIds || {})[reaction].length}{" "}
|
<span className={userReactions.includes(reaction) ? "text-custom-primary-100" : ""}>
|
||||||
</span>
|
{(reactionIds || {})[reaction].length}{" "}
|
||||||
</button>
|
</span>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
@ -184,3 +184,21 @@ export function getChangedIssuefields(formData: Partial<TIssue>, dirtyFields: {
|
|||||||
|
|
||||||
return changedFields;
|
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`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user