feat: follow an epic with everything under it

«Seguir todo» on a ticket with subtasks (and on each epic of the Epics
page) tells you of it and of every ticket hanging from it, at any depth,
also the ones added later: their moves, assignments, comments and plan
changes, as a follower of each, plus when a ticket is placed under it.
Guests of a private project are left out. Personal, no history (Mongo
`tree_follows`).

API: followTree / unfollowTree, Issue.followingTree, PUT/DELETE
/api/issues/{issue}/follow-tree. MCP: watch_issue with tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 19:59:04 +02:00
parent 179d6a5e94
commit d79ef418d3
31 changed files with 1072 additions and 62 deletions

View File

@@ -49,6 +49,7 @@ export const issuePageEvents = eventGroup({
}>(),
watchRequested: type<{ issueId: string; watching: boolean }>(),
pinRequested: type<{ issueId: string; pinned: boolean }>(),
followTreeRequested: type<{ issueId: string; on: boolean }>(),
trashRequested: type<{
issueId: string;
trashed: boolean;

View File

@@ -120,6 +120,13 @@ export class IssueFacade {
);
}
/** Follow the ticket with everything under it (an epic), or stop. */
setFollowingTree(on: boolean): void {
this.withIssue((issueId) =>
this.dispatch.followTreeRequested({ issueId, on }),
);
}
/** Pin the ticket for me (or unpin it): at hand on my home page and in search. */
setPinned(pinned: boolean): void {
this.withIssue((issueId) =>

View File

@@ -379,6 +379,20 @@ export const IssueStore = signalStore(
),
),
),
events
.on(issuePageEvents.followTreeRequested)
.pipe(
mergeMap(({ payload }) =>
runCommand(
issues.followTree(payload.issueId, payload.on),
payload.on
? 'Ahora sigues este ticket con todo lo que cuelga de él'
: 'Ya no sigues lo que cuelga de este ticket',
done,
failed,
),
),
),
events
.on(issuePageEvents.pinRequested)
.pipe(