57 lines
2.0 KiB
TypeScript
57 lines
2.0 KiB
TypeScript
import type { RoomMember } from "./types";
|
|
|
|
/** One row of the `@` list. A null `member` is the whole-room escape hatch. */
|
|
export interface MentionChoice {
|
|
name: string;
|
|
member: RoomMember | null;
|
|
}
|
|
|
|
/**
|
|
* The `@`-token under the caret, or null when the caret is not writing one.
|
|
* A token ends at a space, so `@小美 幫我看` stops offering the list the moment
|
|
* the name is finished, and `mail@example.com` never opens it.
|
|
*/
|
|
export function mentionToken(text: string, caret: number): string | null {
|
|
const end = Math.max(0, Math.min(caret, text.length));
|
|
const hit = /(?:^|\s)@([^\s@]*)$/.exec(text.slice(0, end));
|
|
return hit ? hit[1] : null;
|
|
}
|
|
|
|
/**
|
|
* Who the token could mean, in room order, capped so the list stays one glance.
|
|
* `everyone` — the one name that reaches the whole room — leads the list while
|
|
* the token is empty or could still become it, because typing `@` is where a
|
|
* person discovers that escape hatch.
|
|
*/
|
|
export function mentionChoices(
|
|
token: string,
|
|
members: RoomMember[],
|
|
everyone: string,
|
|
): MentionChoice[] {
|
|
const needle = token.trim().toLowerCase();
|
|
const choices: MentionChoice[] = [];
|
|
if (everyone.toLowerCase().startsWith(needle)) {
|
|
choices.push({ name: everyone, member: null });
|
|
}
|
|
for (const member of members) {
|
|
if (member.name.toLowerCase().includes(needle)) choices.push({ name: member.name, member });
|
|
}
|
|
return choices.slice(0, 6);
|
|
}
|
|
|
|
/**
|
|
* Replace the token being typed with a chosen name and one space, leaving the
|
|
* rest of the message where it was. The new caret sits after that space, so the
|
|
* next keystroke continues the sentence instead of reopening the list.
|
|
*/
|
|
export function acceptMention(
|
|
text: string,
|
|
caret: number,
|
|
name: string,
|
|
): { text: string; caret: number } {
|
|
const end = Math.max(0, Math.min(caret, text.length));
|
|
const head = text.slice(0, end).replace(/@([^\s@]*)$/, "");
|
|
const inserted = `${head}@${name} `;
|
|
return { text: inserted + text.slice(end), caret: inserted.length };
|
|
}
|