mirror of
https://github.com/makeplane/plane
synced 2025-08-07 19:59:33 +00:00
[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:
@@ -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>
|
||||
)}
|
||||
{"."}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./item";
|
||||
export * from "./options";
|
||||
export * from "./content";
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user