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

@@ -109,6 +109,7 @@ export const ReadModels = {
PinnedIssue: 'PinnedIssue',
AlertIntegration: 'AlertIntegration',
AlertTrace: 'AlertTrace',
TreeFollow: 'TreeFollow',
Sprint: 'SprintReadModel',
GitIntegration: 'GitIntegration',
OutgoingWebhook: 'OutgoingWebhook',
@@ -360,6 +361,15 @@ export const AlertTraceSchema =
SchemaFactory.createForClass(AlertTraceDocument);
AlertTraceSchema.index({ projectId: 1, fingerprint: 1 }, { unique: true });
@Schema({ ...LOOSE, collection: 'tree_follows' })
class TreeFollowDocument {
@Prop({ type: String }) userId!: string;
@Prop({ type: String, index: true }) issueId!: string;
}
export const TreeFollowSchema =
SchemaFactory.createForClass(TreeFollowDocument);
TreeFollowSchema.index({ userId: 1, issueId: 1 }, { unique: true });
@Schema({ ...LOOSE, collection: 'git_integrations' })
class GitIntegrationDocument {
/** The project's id. */
@@ -465,6 +475,7 @@ export const READ_MODEL_SCHEMAS = [
{ name: ReadModels.PinnedIssue, schema: PinnedIssueSchema },
{ name: ReadModels.AlertIntegration, schema: AlertIntegrationSchema },
{ name: ReadModels.AlertTrace, schema: AlertTraceSchema },
{ name: ReadModels.TreeFollow, schema: TreeFollowSchema },
{ name: ReadModels.CalendarFeed, schema: CalendarFeedSchema },
{ name: ReadModels.DashboardShare, schema: DashboardShareSchema },
{ name: ReadModels.PushSubscription, schema: PushSubscriptionSchema },