feat: mentions and following tickets

Mention project members in comments with @, follow and unfollow tickets,
and get told when something changes on the tickets you follow. Commenting
on a ticket starts following it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 09:41:32 +02:00
parent efa06a0723
commit 88928b402f
49 changed files with 1318 additions and 41 deletions

View File

@@ -35,6 +35,7 @@ export const issuePageEvents = eventGroup({
/** Uploaded under the new comment once it exists. */
files?: File[];
}>(),
watchRequested: type<{ issueId: string; watching: boolean }>(),
attachRequested: type<{
issueId: string;
files: File[];

View File

@@ -30,6 +30,8 @@ export class IssueFacade {
readonly error = this.store.error;
/** Signed-in user's id (authors can edit their comments). */
readonly me = this.store.me;
/** Whether the signed-in person follows the open ticket. */
readonly watching = this.store.watching;
/** Loads the ticket (by id or key, e.g. BE-3) and follows its changes. */
open(idOrKey: string): void {
@@ -85,6 +87,13 @@ export class IssueFacade {
);
}
/** Follow the ticket (or stop): be told when it moves or gets a comment. */
setWatching(watching: boolean): void {
this.withIssue((issueId) =>
this.dispatch.watchRequested({ issueId, watching }),
);
}
/** Attaches files to the ticket (or under one of its comments). */
attach(files: File[], commentId: string | null = null): void {
if (!files.length) return;

View File

@@ -60,6 +60,11 @@ export const IssueStore = signalStore(
}),
withComputed(({ issue }, auth = inject(AuthFacade)) => ({
me: computed(() => auth.user()?.id ?? null),
watching: computed(
() =>
!!auth.user() &&
(issue()?.watchers ?? []).some((w) => w.id === auth.user()?.id),
),
outwardLinks: computed(
() => issue()?.links.filter((l) => l.direction === 'OUTWARD') ?? [],
),