diff --git a/libs/web/tracker/presenters/src/lib/pages/issue.page.ts b/libs/web/tracker/presenters/src/lib/pages/issue.page.ts index e555df2..7a588eb 100644 --- a/libs/web/tracker/presenters/src/lib/pages/issue.page.ts +++ b/libs/web/tracker/presenters/src/lib/pages/issue.page.ts @@ -608,9 +608,33 @@ const MAX_FILE_BYTES = 25 * 1024 * 1024; novalidate > + @if (replyMentionOptions().length) { +
+ @for (member of replyMentionOptions(); track member.id) { + + } +
+ }
([]); protected readonly segments = mentionSegments; /** Members matching what is typed after an `@` at the end of the comment. */ - protected readonly mentionOptions = computed(() => { - const query = mentionQuery(this.commentBody()); - if (query === null) return []; - return this.project - .members() - .map((m) => m.user) - .filter( - (user) => user.id !== this.facade.me() && matches(user.name, query), - ) - .slice(0, 6); - }); + protected readonly mentionOptions = computed(() => + this.membersMatching(this.commentBody()), + ); + /** The same, for the answer being written. */ + protected readonly replyMentionOptions = computed(() => + this.membersMatching(this.replyBody()), + ); /** Pull requests first, then the newest. */ protected readonly devLinks = computed(() => [...(this.facade.issue()?.devLinks ?? [])].sort( @@ -1226,6 +1246,23 @@ export class IssuePage { this.replyingTo.set(null); } + /** Members whose name fits the `@…` being typed at the end of `text`. */ + private membersMatching(text: string) { + const query = mentionQuery(text); + if (query === null) return []; + return this.project + .members() + .map((m) => m.user) + .filter( + (user) => user.id !== this.facade.me() && matches(user.name, query), + ) + .slice(0, 6); + } + + protected mentionInReply(user: { id: string; name: string }): void { + this.replyBody.update((text) => insertMention(text, user)); + } + protected mention(user: { id: string; name: string }): void { this.commentBody.update((text) => insertMention(text, user)); }