[WEB-4025] fix: external user comment and reaction (#7692)

* chore: reactions types updated

* fix: external user comments

* fix: external user reactions

* chore: added display name for actor

* chore: merge conflicts

* chore: updated the created_by and updated_by

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
This commit is contained in:
Anmol Singh Bhatia 2025-09-09 23:50:11 +05:30 committed by GitHub
parent 260d9a053d
commit 56cd0fc445
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 42 additions and 19 deletions

View file

@ -667,16 +667,33 @@ class IssueReactionSerializer(BaseSerializer):
class IssueReactionLiteSerializer(DynamicBaseSerializer): class IssueReactionLiteSerializer(DynamicBaseSerializer):
display_name = serializers.CharField(source="actor.display_name", read_only=True)
class Meta: class Meta:
model = IssueReaction model = IssueReaction
fields = ["id", "actor", "issue", "reaction"] fields = ["id", "actor", "issue", "reaction", "display_name"]
class CommentReactionSerializer(BaseSerializer): class CommentReactionSerializer(BaseSerializer):
display_name = serializers.CharField(source="actor.display_name", read_only=True)
class Meta: class Meta:
model = CommentReaction model = CommentReaction
fields = "__all__" fields = [
read_only_fields = ["workspace", "project", "comment", "actor", "deleted_at"] "id",
"actor",
"comment",
"reaction",
"display_name",
"deleted_at",
"workspace",
"project",
"created_at",
"updated_at",
"created_by",
"updated_by",
]
read_only_fields = ["workspace", "project", "comment", "actor", "deleted_at", "created_by", "updated_by"]
class IssueVoteSerializer(BaseSerializer): class IssueVoteSerializer(BaseSerializer):

View file

@ -2,9 +2,8 @@ import { FC, ReactNode, useRef } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
// plane imports // plane imports
import { useTranslation } from "@plane/i18n"; import { useTranslation } from "@plane/i18n";
import { Tooltip } from "@plane/propel/tooltip"; import { EIssueCommentAccessSpecifier, TIssueComment } from "@plane/types";
import { TIssueComment } from "@plane/types"; import { Avatar, Tooltip } from "@plane/ui";
import { Avatar } from "@plane/ui";
import { calculateTimeAgo, cn, getFileURL, renderFormattedDate, renderFormattedTime } from "@plane/utils"; import { calculateTimeAgo, cn, getFileURL, renderFormattedDate, renderFormattedTime } from "@plane/utils";
// hooks // hooks
import { useMember } from "@/hooks/store/use-member"; import { useMember } from "@/hooks/store/use-member";
@ -27,7 +26,13 @@ export const CommentBlock: FC<TCommentBlock> = observer((props) => {
// translation // translation
const { t } = useTranslation(); const { t } = useTranslation();
if (!comment || !userDetails) return null; const displayName = comment?.actor_detail?.is_bot
? comment?.actor_detail?.first_name + ` ${t("bot")}`
: (userDetails?.display_name ?? comment?.actor_detail?.display_name);
const avatarUrl = userDetails?.avatar_url ?? comment?.actor_detail?.avatar_url;
if (!comment) return null;
return ( return (
<div <div
@ -43,20 +48,15 @@ export const CommentBlock: FC<TCommentBlock> = observer((props) => {
"flex-shrink-0 relative w-7 h-6 rounded-full transition-border duration-1000 flex justify-center items-center z-[3] uppercase font-medium" "flex-shrink-0 relative w-7 h-6 rounded-full transition-border duration-1000 flex justify-center items-center z-[3] uppercase font-medium"
)} )}
> >
<Avatar <Avatar size="base" name={displayName} src={getFileURL(avatarUrl)} className="flex-shrink-0" />
size="base"
name={userDetails?.display_name}
src={getFileURL(userDetails?.avatar_url)}
className="flex-shrink-0"
/>
</div> </div>
<div className="flex flex-col gap-3 truncate flex-grow"> <div className="flex flex-col gap-3 truncate flex-grow">
<div className="flex w-full gap-2"> <div className="flex w-full gap-2">
<div className="flex-1 flex flex-wrap items-center gap-1"> <div className="flex-1 flex flex-wrap items-center gap-1">
<div className="text-xs font-medium"> <div className="flex items-center gap-1">
{comment?.actor_detail?.is_bot <span className="text-xs font-medium">
? comment?.actor_detail?.first_name + ` ${t("bot")}` {`${displayName}${comment.access === EIssueCommentAccessSpecifier.EXTERNAL ? " (External User)" : ""}`}
: comment?.actor_detail?.display_name || userDetails.display_name} </span>
</div> </div>
<div className="text-xs text-custom-text-300"> <div className="text-xs text-custom-text-300">
commented{" "} commented{" "}

View file

@ -85,7 +85,9 @@ export const IssueCommentReaction: FC<TIssueCommentReaction> = observer((props)
const reactionUsers = (reactionIds?.[reaction] || []) const reactionUsers = (reactionIds?.[reaction] || [])
.map((reactionId) => { .map((reactionId) => {
const reactionDetails = getCommentReactionById(reactionId); const reactionDetails = getCommentReactionById(reactionId);
return reactionDetails ? getUserDetails(reactionDetails.actor)?.display_name : null; return reactionDetails
? getUserDetails(reactionDetails?.actor)?.display_name || reactionDetails?.display_name
: null;
}) })
.filter((displayName): displayName is string => !!displayName); .filter((displayName): displayName is string => !!displayName);
const formattedUsers = formatTextList(reactionUsers); const formattedUsers = formatTextList(reactionUsers);

View file

@ -85,7 +85,9 @@ export const IssueReaction: FC<TIssueReaction> = observer((props) => {
const reactionUsers = (reactionIds?.[reaction] || []) const reactionUsers = (reactionIds?.[reaction] || [])
.map((reactionId) => { .map((reactionId) => {
const reactionDetails = getReactionById(reactionId); const reactionDetails = getReactionById(reactionId);
return reactionDetails ? getUserDetails(reactionDetails.actor)?.display_name : null; return reactionDetails
? getUserDetails(reactionDetails?.actor)?.display_name || reactionDetails?.display_name
: null;
}) })
.filter((displayName): displayName is string => !!displayName); .filter((displayName): displayName is string => !!displayName);

View file

@ -9,6 +9,7 @@ export type TIssueCommentReaction = {
updated_at: Date; updated_at: Date;
created_by: string; created_by: string;
updated_by: string; updated_by: string;
display_name: string;
}; };
export type TIssueCommentReactionMap = { export type TIssueCommentReactionMap = {

View file

@ -5,6 +5,7 @@ export type TIssueReaction = {
id: string; id: string;
issue: string; issue: string;
reaction: string; reaction: string;
display_name: string;
}; };
export interface IIssuePublicReaction { export interface IIssuePublicReaction {