cb9a0b9250
- contract: SendOpts.mentions (opaque userId notify-list) + optional listMembers(threadId) for autocomplete/highlight (capability-degrading) - mock: listMembers; KernelClientAdapter.send forwards mentions to the socket (which already carries them → inbox MENTION items) - composer: type @ → member/@channel/@here autocomplete; Enter picks the first; on send, mentions resolve to ids; message bodies highlight @mentions - useMembers hook; mentions.tsx helpers (trailingMentionQuery/insertMention/ resolveMentions/highlightMentions) - 4 mention tests (helpers + render autocomplete→send carries id); 56 total green Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
32 lines
820 B
TypeScript
32 lines
820 B
TypeScript
import { useEffect, useState } from 'react';
|
|
import { useAdapter } from '../provider';
|
|
import type { Person } from '../types';
|
|
|
|
/** A thread's members (for @mention autocomplete + highlighting). Empty if the adapter
|
|
* doesn't implement listMembers, or while loading. */
|
|
export function useMembers(threadId: string | null): Person[] {
|
|
const adapter = useAdapter();
|
|
const [members, setMembers] = useState<Person[]>([]);
|
|
|
|
useEffect(() => {
|
|
if (!threadId || !adapter.listMembers) {
|
|
setMembers([]);
|
|
return;
|
|
}
|
|
let alive = true;
|
|
adapter
|
|
.listMembers(threadId)
|
|
.then((m) => {
|
|
if (alive) setMembers(m);
|
|
})
|
|
.catch(() => {
|
|
if (alive) setMembers([]);
|
|
});
|
|
return () => {
|
|
alive = false;
|
|
};
|
|
}, [adapter, threadId]);
|
|
|
|
return members;
|
|
}
|