Show agent presence on direct-message avatars
chat_presence was recorded (active, waiting_approval, thinking) but never surfaced. Map each status to a colour and render a presence dot on the counterpart's avatar in the direct-message list, matching the design.
This commit is contained in:
1 parent
b4056502ec
commit
0299ebbade
3 files changed
+46
-2
No files matched your search
@@ -214,6 +214,7 @@ export function ChatPage({ snapshot, actions }: PageProps) {
|
||||
directs={directs}
|
||||
agents={agents}
|
||||
messages={messagesAll}
|
||||
presence={snapshot.chatPresence}
|
||||
activeSpaceId={activeSpaceId}
|
||||
onSelect={(spaceId) => {
|
||||
setSelectedSpaceId(spaceId);
|
||||
|
||||
@@ -1,13 +1,29 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { Avatar, Icon, IconButton } from "../../shared/ui";
|
||||
import type { AgentRecord, ChatMessageRecord, ChatSpaceRecord } from "../../../../shared/domain";
|
||||
import type { AgentRecord, ChatMessageRecord, ChatPresenceRecord, ChatSpaceRecord } from "../../../../shared/domain";
|
||||
import { countMessagesIn } from "./chat-helpers";
|
||||
|
||||
function presenceColor(status: string | undefined): string | null {
|
||||
switch (status) {
|
||||
case "active":
|
||||
case "online":
|
||||
return "var(--green)";
|
||||
case "waiting_approval":
|
||||
case "busy":
|
||||
return "var(--amber)";
|
||||
case "thinking":
|
||||
return "var(--accent)";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function ChatSidebar({
|
||||
channels,
|
||||
directs,
|
||||
agents,
|
||||
messages,
|
||||
presence,
|
||||
activeSpaceId,
|
||||
onSelect,
|
||||
onCreateSpace,
|
||||
@@ -16,10 +32,12 @@ export function ChatSidebar({
|
||||
directs: ChatSpaceRecord[];
|
||||
agents: AgentRecord[];
|
||||
messages: ChatMessageRecord[];
|
||||
presence: ChatPresenceRecord[];
|
||||
activeSpaceId: string | null;
|
||||
onSelect: (spaceId: string) => void;
|
||||
onCreateSpace: () => void;
|
||||
}) {
|
||||
const presenceByPrincipal = new Map(presence.map((entry) => [entry.principalId, entry.status]));
|
||||
return (
|
||||
<aside className="secondary-pane">
|
||||
<div className="row" style={{ padding: "4px 8px 12px" }}>
|
||||
@@ -47,6 +65,7 @@ export function ChatSidebar({
|
||||
key={space.id}
|
||||
space={space}
|
||||
agents={agents}
|
||||
presenceByPrincipal={presenceByPrincipal}
|
||||
active={space.id === activeSpaceId}
|
||||
count={countMessagesIn(space.id, messages)}
|
||||
onClick={() => onSelect(space.id)}
|
||||
@@ -95,20 +114,30 @@ function ChannelItem({
|
||||
function DirectItem({
|
||||
space,
|
||||
agents,
|
||||
presenceByPrincipal,
|
||||
active,
|
||||
count,
|
||||
onClick,
|
||||
}: {
|
||||
space: ChatSpaceRecord;
|
||||
agents: AgentRecord[];
|
||||
presenceByPrincipal: Map<string, string>;
|
||||
active: boolean;
|
||||
count: number;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
const counterpart = agents.find((agent) => space.name.includes(agent.name));
|
||||
const dotColor = counterpart ? presenceColor(presenceByPrincipal.get(counterpart.id)) : null;
|
||||
return (
|
||||
<button type="button" className={`nav-item ${active ? "active" : ""}`.trim()} onClick={onClick}>
|
||||
{counterpart ? <Avatar name={counterpart.name} size="sm" bot /> : <Icon name="user" size={13} />}
|
||||
{counterpart ? (
|
||||
<span className="presence">
|
||||
<Avatar name={counterpart.name} size="sm" bot />
|
||||
{dotColor && <span className="presence-dot" style={{ background: dotColor }} />}
|
||||
</span>
|
||||
) : (
|
||||
<Icon name="user" size={13} />
|
||||
)}
|
||||
<span>{space.name}</span>
|
||||
{count > 0 && <span className="nav-badge">{count}</span>}
|
||||
</button>
|
||||
|
||||
@@ -311,6 +311,20 @@ a { color: inherit; }
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.presence {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.presence-dot {
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
bottom: -1px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid var(--bg);
|
||||
}
|
||||
.page { max-width: 1100px; }
|
||||
.page-wide { width: 100%; }
|
||||
.page-h1 {
|
||||
|
||||
Reference in new issue
Block a user