MUL-5127: show issue agent activity in Inbox (#5737)

* feat(inbox): show agent status in list (MUL-5127)

Co-authored-by: multica-agent <github@multica.ai>

* fix(inbox): show issue agent activity (MUL-5127)

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Lambda <lambda@multica.ai>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
Jiayuan Zhang
2026-07-23 01:32:43 +08:00
committed by GitHub
parent d4391fda4d
commit f2ef7ab0d1
2 changed files with 58 additions and 5 deletions

View File

@@ -4,7 +4,29 @@ import type { InboxItem } from "@multica/core/types";
import { InboxListItem } from "./inbox-list-item";
vi.mock("../../issues/components", () => ({ StatusIcon: () => null }));
vi.mock("../../common/actor-avatar", () => ({ ActorAvatar: () => null }));
vi.mock("../../issues/components/issue-agent-activity-indicator", () => ({
IssueAgentActivityIndicator: ({ issueId }: { issueId: string }) => (
<span data-testid="issue-agent-activity" data-issue-id={issueId} />
),
}));
vi.mock("../../common/actor-avatar", () => ({
ActorAvatar: ({
actorType,
actorId,
showStatusDot,
}: {
actorType: string;
actorId: string;
showStatusDot?: boolean;
}) => (
<span
data-testid="actor-avatar"
data-actor-type={actorType}
data-actor-id={actorId}
data-show-status-dot={showStatusDot === true ? "true" : "false"}
/>
),
}));
vi.mock("./inbox-detail-label", () => ({ InboxDetailLabel: () => null }));
vi.mock("../../i18n", () => ({ useT: () => ({ t: () => "label" }) }));
@@ -72,3 +94,25 @@ describe("InboxListItem unread affordance", () => {
expect(title(container)?.className).not.toContain("font-medium");
});
});
describe("InboxListItem issue activity", () => {
it("shows issue-specific agent activity without an availability dot", () => {
const { getByTestId } = renderRow({ item: item(), view: "inbox" });
expect(getByTestId("actor-avatar").getAttribute("data-show-status-dot")).toBe(
"false",
);
expect(
getByTestId("issue-agent-activity").getAttribute("data-issue-id"),
).toBe("issue-1");
});
it("omits issue activity for a notification without an issue", () => {
const { queryByTestId } = renderRow({
item: item({ issue_id: null }),
view: "inbox",
});
expect(queryByTestId("issue-agent-activity")).toBeNull();
});
});

View File

@@ -1,6 +1,9 @@
"use client";
import { StatusIcon } from "../../issues/components";
import {
IssueAgentActivityIndicator,
} from "../../issues/components/issue-agent-activity-indicator";
import { ActorAvatar } from "../../common/actor-avatar";
import { Archive, ArchiveRestore } from "lucide-react";
import type { InboxItem } from "@multica/core/types";
@@ -53,6 +56,7 @@ export function InboxListItem({
const actionLabel = isArchivedView
? t(($) => $.list.unarchive_tooltip)
: t(($) => $.list.archive_tooltip);
const actorType = item.actor_type ?? item.recipient_type;
return (
<button
@@ -63,7 +67,7 @@ export function InboxListItem({
}`}
>
<ActorAvatar
actorType={item.actor_type ?? item.recipient_type}
actorType={actorType}
actorId={item.actor_id ?? item.recipient_id}
size="lg"
enableHoverCard
@@ -109,9 +113,14 @@ export function InboxListItem({
<p className={`min-w-0 overflow-hidden text-ellipsis whitespace-nowrap text-xs ${showUnread ? "text-muted-foreground" : "text-muted-foreground/60"}`}>
<InboxDetailLabel item={item} />
</p>
<span className={`shrink-0 text-xs ${showUnread ? "text-muted-foreground" : "text-muted-foreground/60"}`}>
{timeAgo(item.created_at)}
</span>
<div className="flex shrink-0 items-center gap-1.5">
{item.issue_id && (
<IssueAgentActivityIndicator issueId={item.issue_id} />
)}
<span className={`text-xs ${showUnread ? "text-muted-foreground" : "text-muted-foreground/60"}`}>
{timeAgo(item.created_at)}
</span>
</div>
</div>
</div>
</button>