feat(iios): include participant names in listThreads (generic)
GET /v1/threads now returns participants[] (member usernames) so a host app can render conversation titles / member lists without a second call. Still generic — it only lists members of threads the caller belongs to. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ export interface ThreadSummary {
|
|||||||
threadId: string;
|
threadId: string;
|
||||||
subject: string | null;
|
subject: string | null;
|
||||||
membership?: string;
|
membership?: string;
|
||||||
|
participants: string[];
|
||||||
participantCount: number;
|
participantCount: number;
|
||||||
unread: number;
|
unread: number;
|
||||||
lastMessage?: string;
|
lastMessage?: string;
|
||||||
@@ -135,11 +136,20 @@ export class MessageService {
|
|||||||
const threadIds = memberships.map((m) => m.threadId);
|
const threadIds = memberships.map((m) => m.threadId);
|
||||||
if (threadIds.length === 0) return [];
|
if (threadIds.length === 0) return [];
|
||||||
|
|
||||||
const [threads, unreads] = await Promise.all([
|
const [threads, unreads, allParts] = await Promise.all([
|
||||||
this.prisma.iiosThread.findMany({ where: { id: { in: threadIds } }, include: { _count: { select: { participants: true } } } }),
|
this.prisma.iiosThread.findMany({ where: { id: { in: threadIds } } }),
|
||||||
this.prisma.iiosUnreadCounter.findMany({ where: { threadId: { in: threadIds }, actorId: actor.id } }),
|
this.prisma.iiosUnreadCounter.findMany({ where: { threadId: { in: threadIds }, actorId: actor.id } }),
|
||||||
|
this.prisma.iiosThreadParticipant.findMany({
|
||||||
|
where: { threadId: { in: threadIds } },
|
||||||
|
include: { actor: { include: { sourceHandle: true } } },
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
const unreadBy = new Map(unreads.map((u) => [u.threadId, u.unreadCount]));
|
const unreadBy = new Map(unreads.map((u) => [u.threadId, u.unreadCount]));
|
||||||
|
const membersBy = new Map<string, string[]>();
|
||||||
|
for (const p of allParts) {
|
||||||
|
const name = p.actor?.sourceHandle?.externalId ?? p.actor?.displayName ?? p.actorId;
|
||||||
|
membersBy.set(p.threadId, [...(membersBy.get(p.threadId) ?? []), name]);
|
||||||
|
}
|
||||||
|
|
||||||
const summaries = await Promise.all(
|
const summaries = await Promise.all(
|
||||||
threads.map(async (t) => {
|
threads.map(async (t) => {
|
||||||
@@ -148,11 +158,13 @@ export class MessageService {
|
|||||||
orderBy: { occurredAt: 'desc' },
|
orderBy: { occurredAt: 'desc' },
|
||||||
include: { parts: { where: { kind: 'TEXT' }, take: 1 } },
|
include: { parts: { where: { kind: 'TEXT' }, take: 1 } },
|
||||||
});
|
});
|
||||||
|
const members = membersBy.get(t.id) ?? [];
|
||||||
return {
|
return {
|
||||||
threadId: t.id,
|
threadId: t.id,
|
||||||
subject: t.subject,
|
subject: t.subject,
|
||||||
membership: (t.metadata as { membership?: string } | null)?.membership,
|
membership: (t.metadata as { membership?: string } | null)?.membership,
|
||||||
participantCount: t._count.participants,
|
participants: members,
|
||||||
|
participantCount: members.length,
|
||||||
unread: unreadBy.get(t.id) ?? 0,
|
unread: unreadBy.get(t.id) ?? 0,
|
||||||
lastMessage: last?.parts[0]?.bodyText ?? undefined,
|
lastMessage: last?.parts[0]?.bodyText ?? undefined,
|
||||||
lastAt: last?.occurredAt,
|
lastAt: last?.occurredAt,
|
||||||
|
|||||||
Reference in New Issue
Block a user