ui: avoid duplicate and self comment toasts
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useRef, type ReactNode } from "react";
|
import { useEffect, useRef, type ReactNode } from "react";
|
||||||
import { useQueryClient, type QueryClient } from "@tanstack/react-query";
|
import { useQuery, useQueryClient, type QueryClient } from "@tanstack/react-query";
|
||||||
import type { Agent, Issue, LiveEvent } from "@paperclipai/shared";
|
import type { Agent, Issue, LiveEvent } from "@paperclipai/shared";
|
||||||
|
import { authApi } from "../api/auth";
|
||||||
import { useCompany } from "./CompanyContext";
|
import { useCompany } from "./CompanyContext";
|
||||||
import type { ToastInput } from "./ToastContext";
|
import type { ToastInput } from "./ToastContext";
|
||||||
import { useToast } from "./ToastContext";
|
import { useToast } from "./ToastContext";
|
||||||
@@ -152,6 +153,7 @@ function buildActivityToast(
|
|||||||
queryClient: QueryClient,
|
queryClient: QueryClient,
|
||||||
companyId: string,
|
companyId: string,
|
||||||
payload: Record<string, unknown>,
|
payload: Record<string, unknown>,
|
||||||
|
currentActor: { userId: string | null; agentId: string | null },
|
||||||
): ToastInput | null {
|
): ToastInput | null {
|
||||||
const entityType = readString(payload.entityType);
|
const entityType = readString(payload.entityType);
|
||||||
const entityId = readString(payload.entityId);
|
const entityId = readString(payload.entityId);
|
||||||
@@ -200,6 +202,11 @@ function buildActivityToast(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const commentId = readString(details?.commentId);
|
const commentId = readString(details?.commentId);
|
||||||
|
const isSelfComment =
|
||||||
|
action === "issue.comment_added" &&
|
||||||
|
((actorType === "user" && !!currentActor.userId && actorId === currentActor.userId) ||
|
||||||
|
(actorType === "agent" && !!currentActor.agentId && actorId === currentActor.agentId));
|
||||||
|
if (isSelfComment) return null;
|
||||||
const bodySnippet = readString(details?.bodySnippet);
|
const bodySnippet = readString(details?.bodySnippet);
|
||||||
const reopened = details?.reopened === true;
|
const reopened = details?.reopened === true;
|
||||||
const reopenedFrom = readString(details?.reopenedFrom);
|
const reopenedFrom = readString(details?.reopenedFrom);
|
||||||
@@ -448,6 +455,7 @@ function handleLiveEvent(
|
|||||||
event: LiveEvent,
|
event: LiveEvent,
|
||||||
pushToast: (toast: ToastInput) => string | null,
|
pushToast: (toast: ToastInput) => string | null,
|
||||||
gate: ToastGate,
|
gate: ToastGate,
|
||||||
|
currentActor: { userId: string | null; agentId: string | null },
|
||||||
) {
|
) {
|
||||||
if (event.companyId !== expectedCompanyId) return;
|
if (event.companyId !== expectedCompanyId) return;
|
||||||
|
|
||||||
@@ -485,7 +493,7 @@ function handleLiveEvent(
|
|||||||
invalidateActivityQueries(queryClient, expectedCompanyId, payload);
|
invalidateActivityQueries(queryClient, expectedCompanyId, payload);
|
||||||
const action = readString(payload.action);
|
const action = readString(payload.action);
|
||||||
const toast =
|
const toast =
|
||||||
buildActivityToast(queryClient, expectedCompanyId, payload) ??
|
buildActivityToast(queryClient, expectedCompanyId, payload, currentActor) ??
|
||||||
buildJoinRequestToast(payload);
|
buildJoinRequestToast(payload);
|
||||||
if (toast) gatedPushToast(gate, pushToast, `activity:${action ?? "unknown"}`, toast);
|
if (toast) gatedPushToast(gate, pushToast, `activity:${action ?? "unknown"}`, toast);
|
||||||
}
|
}
|
||||||
@@ -496,6 +504,12 @@ export function LiveUpdatesProvider({ children }: { children: ReactNode }) {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { pushToast } = useToast();
|
const { pushToast } = useToast();
|
||||||
const gateRef = useRef<ToastGate>({ cooldownHits: new Map(), suppressUntil: 0 });
|
const gateRef = useRef<ToastGate>({ cooldownHits: new Map(), suppressUntil: 0 });
|
||||||
|
const { data: session } = useQuery({
|
||||||
|
queryKey: queryKeys.auth.session,
|
||||||
|
queryFn: () => authApi.getSession(),
|
||||||
|
retry: false,
|
||||||
|
});
|
||||||
|
const currentUserId = session?.user?.id ?? session?.session?.userId ?? null;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!selectedCompanyId) return;
|
if (!selectedCompanyId) return;
|
||||||
@@ -541,7 +555,10 @@ export function LiveUpdatesProvider({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(raw) as LiveEvent;
|
const parsed = JSON.parse(raw) as LiveEvent;
|
||||||
handleLiveEvent(queryClient, selectedCompanyId, parsed, pushToast, gateRef.current);
|
handleLiveEvent(queryClient, selectedCompanyId, parsed, pushToast, gateRef.current, {
|
||||||
|
userId: currentUserId,
|
||||||
|
agentId: null,
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore non-JSON payloads.
|
// Ignore non-JSON payloads.
|
||||||
}
|
}
|
||||||
@@ -570,7 +587,7 @@ export function LiveUpdatesProvider({ children }: { children: ReactNode }) {
|
|||||||
socket.close(1000, "provider_unmount");
|
socket.close(1000, "provider_unmount");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [queryClient, selectedCompanyId, pushToast]);
|
}, [queryClient, selectedCompanyId, pushToast, currentUserId]);
|
||||||
|
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -419,17 +419,9 @@ export function IssueDetail() {
|
|||||||
const addComment = useMutation({
|
const addComment = useMutation({
|
||||||
mutationFn: ({ body, reopen }: { body: string; reopen?: boolean }) =>
|
mutationFn: ({ body, reopen }: { body: string; reopen?: boolean }) =>
|
||||||
issuesApi.addComment(issueId!, body, reopen),
|
issuesApi.addComment(issueId!, body, reopen),
|
||||||
onSuccess: (comment) => {
|
onSuccess: () => {
|
||||||
invalidateIssue();
|
invalidateIssue();
|
||||||
queryClient.invalidateQueries({ queryKey: queryKeys.issues.comments(issueId!) });
|
queryClient.invalidateQueries({ queryKey: queryKeys.issues.comments(issueId!) });
|
||||||
const issueRef = issue?.identifier ?? (issueId ? `Issue ${issueId.slice(0, 8)}` : "Issue");
|
|
||||||
pushToast({
|
|
||||||
dedupeKey: `activity:issue.comment_added:${issueId}:${comment.id}`,
|
|
||||||
title: `Comment posted on ${issueRef}`,
|
|
||||||
body: issue?.title ? truncate(issue.title, 96) : undefined,
|
|
||||||
tone: "success",
|
|
||||||
action: issueId ? { label: `View ${issueRef}`, href: `/issues/${issue?.identifier ?? issueId}` } : undefined,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user