[WEB-3262]fix: incomplete activity render for activity in notifications (#6777)

* fix: incomplete activity render for activity in notifications

* fix: handled content overflow for long notification messages
This commit is contained in:
Vamsi Krishna
2025-03-20 14:02:06 +05:30
committed by GitHub
parent 433682e913
commit ca5cf27957
3 changed files with 122 additions and 54 deletions

View File

@@ -0,0 +1,109 @@
import { FC } from "react";
import { TNotification } from "@plane/types";
// components
import { LiteTextReadOnlyEditor } from "@/components/editor";
// helpers
import { renderFormattedDate } from "@/helpers/date-time.helper";
import { sanitizeCommentForNotification } from "@/helpers/notification.helper";
import { replaceUnderscoreIfSnakeCase, stripAndTruncateHTML } from "@/helpers/string.helper";
export const NotificationContent: FC<{
notification: TNotification;
workspaceId: string;
workspaceSlug: string;
projectId: string;
renderCommentBox?: boolean;
}> = ({ notification, workspaceId, workspaceSlug, projectId, renderCommentBox = false }) => {
const { data, triggered_by_details: triggeredBy } = notification;
const notificationField = data?.issue_activity.field;
const newValue = data?.issue_activity.new_value;
const oldValue = data?.issue_activity.old_value;
const verb = data?.issue_activity.verb;
const renderTriggerName = () => (
<span className="text-custom-text-100 font-medium">
{triggeredBy?.is_bot ? triggeredBy.first_name : triggeredBy?.display_name}{" "}
</span>
);
const renderAction = () => {
if (!notificationField) return "";
if (notificationField === "duplicate")
return verb === "created"
? "marked that this work item is a duplicate of"
: "marked that this work item is not a duplicate";
if (notificationField === "assignees") {
return newValue !== "" ? "added assignee" : "removed assignee";
}
if (notificationField === "start_date") {
return newValue !== "" ? "set start date" : "removed the start date";
}
if (notificationField === "target_date") {
return newValue !== "" ? "set due date" : "removed the due date";
}
if (notificationField === "labels") {
return newValue !== "" ? "added label" : "removed label";
}
if (notificationField === "parent") {
return newValue !== "" ? "added parent" : "removed parent";
}
if (notificationField === "relates_to") return "marked that this work item is related to";
if (notificationField === "comment") return "commented";
if (notificationField === "archived_at") {
return newValue === "restore" ? "restored the work item" : "archived the work item";
}
if (notificationField === "None") return null;
const baseAction = !["comment", "archived_at"].includes(notificationField) ? verb : "";
return `${baseAction} ${replaceUnderscoreIfSnakeCase(notificationField)}`;
};
const renderValue = () => {
if (notificationField === "None") return "the work item and assigned it to you.";
if (notificationField === "comment") return renderCommentBox ? null : sanitizeCommentForNotification(newValue);
if (notificationField === "target_date" || notificationField === "start_date") return renderFormattedDate(newValue);
if (notificationField === "attachment") return "the work item";
if (notificationField === "description") return stripAndTruncateHTML(newValue || "", 55);
if (notificationField === "archived_at") return null;
if (notificationField === "assignees") return newValue !== "" ? newValue : oldValue;
if (notificationField === "labels") return newValue !== "" ? newValue : oldValue;
if (notificationField === "parent") return newValue !== "" ? newValue : oldValue;
return newValue;
};
const shouldShowConnector = ![
"comment",
"archived_at",
"None",
"assignees",
"labels",
"start_date",
"target_date",
"parent",
].includes(notificationField || "");
return (
<>
{renderTriggerName()}
<span className="text-custom-text-300">{renderAction()} </span>
{verb !== "deleted" && (
<>
{shouldShowConnector && <span className="text-custom-text-300">to </span>}
<span className="text-custom-text-100 font-medium">{renderValue()}</span>
{notificationField === "comment" && renderCommentBox && (
<div className="scale-75 origin-left">
<LiteTextReadOnlyEditor
id=""
initialValue={newValue ?? ""}
workspaceId={workspaceId}
workspaceSlug={workspaceSlug}
projectId={projectId}
/>
</div>
)}
{"."}
</>
)}
</>
);
};

View File

@@ -1,2 +1,3 @@
export * from "./item";
export * from "./options";
export * from "./content";

View File

@@ -10,10 +10,9 @@ import { NotificationOption } from "@/components/workspace-notifications";
import { cn } from "@/helpers/common.helper";
import { calculateTimeAgo, renderFormattedDate, renderFormattedTime } from "@/helpers/date-time.helper";
import { getFileURL } from "@/helpers/file.helper";
import { sanitizeCommentForNotification } from "@/helpers/notification.helper";
import { replaceUnderscoreIfSnakeCase, stripAndTruncateHTML } from "@/helpers/string.helper";
// hooks
import { useIssueDetail, useNotification, useWorkspaceNotifications } from "@/hooks/store";
import { useIssueDetail, useNotification, useWorkspace, useWorkspaceNotifications } from "@/hooks/store";
import { NotificationContent } from "./content";
type TNotificationItem = {
workspaceSlug: string;
@@ -26,6 +25,7 @@ export const NotificationItem: FC<TNotificationItem> = observer((props) => {
const { currentSelectedNotificationId, setCurrentSelectedNotificationId } = useWorkspaceNotifications();
const { asJson: notification, markNotificationAsRead } = useNotification(notificationId);
const { getIsIssuePeeked, setPeekIssue } = useIssueDetail();
const { getWorkspaceBySlug } = useWorkspace();
// states
const [isSnoozeStateModalOpen, setIsSnoozeStateModalOpen] = useState(false);
const [customSnoozeModal, setCustomSnoozeModal] = useState(false);
@@ -33,6 +33,7 @@ export const NotificationItem: FC<TNotificationItem> = observer((props) => {
// derived values
const projectId = notification?.project || undefined;
const issueId = notification?.data?.issue?.id || undefined;
const workspace = getWorkspaceBySlug(workspaceSlug);
const notificationField = notification?.data?.issue_activity.field || undefined;
const notificationTriggeredBy = notification.triggered_by_details || undefined;
@@ -57,7 +58,8 @@ export const NotificationItem: FC<TNotificationItem> = observer((props) => {
}
};
if (!workspaceSlug || !notificationId || !notification?.id || !notificationField) return <></>;
if (!workspaceSlug || !notificationId || !notification?.id || !notificationField || !workspace?.id || !projectId)
return <></>;
return (
<Row
@@ -88,56 +90,12 @@ export const NotificationItem: FC<TNotificationItem> = observer((props) => {
<div className="w-full space-y-1 -mt-2">
<div className="relative flex items-center gap-3 h-8">
<div className="w-full overflow-hidden whitespace-normal break-all truncate line-clamp-1 text-sm text-custom-text-100">
{!notification.message ? (
<>
<span className="font-semibold">
{notificationTriggeredBy?.is_bot
? notificationTriggeredBy?.first_name
: notificationTriggeredBy?.display_name}{" "}
</span>
{!["comment", "archived_at"].includes(notificationField) && notification?.data?.issue_activity.verb}{" "}
{notificationField === "comment"
? "commented"
: notificationField === "archived_at"
? notification?.data?.issue_activity.new_value === "restore"
? "restored the work item"
: "archived the work item"
: notificationField === "None"
? null
: replaceUnderscoreIfSnakeCase(notificationField)}{" "}
{notification?.data?.issue_activity.verb !== "deleted" && (
<>
{!["comment", "archived_at", "None"].includes(notificationField) ? "to" : ""}
<span className="font-semibold">
{" "}
{notificationField !== "None" ? (
notificationField !== "comment" ? (
notificationField === "target_date" ? (
renderFormattedDate(notification?.data?.issue_activity.new_value)
) : notificationField === "attachment" ? (
"the work item"
) : notificationField === "description" ? (
stripAndTruncateHTML(notification?.data?.issue_activity.new_value || "", 55)
) : notificationField === "archived_at" ? null : (
notification?.data?.issue_activity.new_value
)
) : (
<span>
{sanitizeCommentForNotification(
notification?.data?.issue_activity.new_value ?? undefined
)}
</span>
)
) : (
"the work item and assigned it to you."
)}
</span>
</>
)}
</>
) : (
<span className="semi-bold">{notification.message}</span>
)}
<NotificationContent
notification={notification}
workspaceId={workspace.id}
workspaceSlug={workspaceSlug}
projectId={projectId}
/>
</div>
<NotificationOption
workspaceSlug={workspaceSlug}