Opening a ticket notes the visit and answers the one before (issue_visits), so the page marks others' comments and changes since. A comment can become a ticket of its own, as a subtask or linked; it remembers where it went. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
153 lines
4.8 KiB
TypeScript
153 lines
4.8 KiB
TypeScript
import { type } from '@ngrx/signals';
|
|
import { eventGroup } from '@ngrx/signals/events';
|
|
import {
|
|
Activity,
|
|
ChangeStatusInput,
|
|
Comment,
|
|
CommentChanged,
|
|
Issue,
|
|
IssueSummary,
|
|
LinkIssuesInput,
|
|
UpdateIssueInput,
|
|
ReviewVerdict,
|
|
} from '@task-stream/web/tracker/domain';
|
|
|
|
export const issuePageEvents = eventGroup({
|
|
source: 'Issue Page',
|
|
events: {
|
|
opened: type<{ idOrKey: string }>(),
|
|
/** `undoing`: it is the «Deshacer» of a change, which offers none. */
|
|
statusChangeRequested: type<ChangeStatusInput & { undoing?: boolean }>(),
|
|
assignRequested: type<{
|
|
issueId: string;
|
|
assigneeId: string | null;
|
|
undoing?: boolean;
|
|
}>(),
|
|
updateRequested: type<UpdateIssueInput>(),
|
|
linkRequested: type<LinkIssuesInput>(),
|
|
/** Relate by the other ticket's key (e.g. BE-3), resolved to its id first. */
|
|
linkByKeyRequested: type<{
|
|
issueId: string;
|
|
type: LinkIssuesInput['type'];
|
|
targetKey: string;
|
|
}>(),
|
|
unlinkRequested: type<LinkIssuesInput>(),
|
|
sprintChangeRequested: type<{
|
|
issueId: string;
|
|
sprintId: string | null;
|
|
}>(),
|
|
releaseChangeRequested: type<{
|
|
issueId: string;
|
|
releaseId: string | null;
|
|
}>(),
|
|
commentAddRequested: type<{
|
|
issueId: string;
|
|
body: string;
|
|
/** Uploaded under the new comment once it exists. */
|
|
files?: File[];
|
|
/** Comment it answers (one level of replies). */
|
|
replyTo?: string | null;
|
|
/** A quick poll's options. */
|
|
poll?: string[] | null;
|
|
}>(),
|
|
pollVoteRequested: type<{ commentId: string; option: number | null }>(),
|
|
pollCloseRequested: type<{ commentId: string; decide: boolean }>(),
|
|
watchRequested: type<{ issueId: string; watching: boolean }>(),
|
|
pinRequested: type<{ issueId: string; pinned: boolean }>(),
|
|
followTreeRequested: type<{ issueId: string; on: boolean }>(),
|
|
reviewRequested: type<{
|
|
issueId: string;
|
|
reviewerId: string;
|
|
note: string | null;
|
|
}>(),
|
|
reviewAnswerRequested: type<{
|
|
issueId: string;
|
|
reviewId: string;
|
|
verdict: ReviewVerdict;
|
|
answer: string | null;
|
|
}>(),
|
|
reviewCancelRequested: type<{ issueId: string; reviewId: string }>(),
|
|
reminderRequested: type<{
|
|
issueId: string;
|
|
/** null: drop it. */
|
|
at: string | null;
|
|
note: string | null;
|
|
}>(),
|
|
trashRequested: type<{
|
|
issueId: string;
|
|
trashed: boolean;
|
|
undoing?: boolean;
|
|
}>(),
|
|
/** Place the ticket under another one by its key (e.g. WEB-3); null: out. */
|
|
parentRequested: type<{ issueId: string; parentKey: string | null }>(),
|
|
subtaskCreateRequested: type<{
|
|
projectId: string;
|
|
parentId: string;
|
|
type: string;
|
|
title: string;
|
|
}>(),
|
|
checklistAddRequested: type<{ issueId: string; text: string }>(),
|
|
checklistUpdateRequested: type<{
|
|
issueId: string;
|
|
itemId: string;
|
|
changes: { text?: string; done?: boolean };
|
|
}>(),
|
|
checklistRemoveRequested: type<{ issueId: string; itemId: string }>(),
|
|
attachRequested: type<{
|
|
issueId: string;
|
|
files: File[];
|
|
commentId?: string | null;
|
|
}>(),
|
|
attachmentRemoveRequested: type<{
|
|
issueId: string;
|
|
attachmentId: string;
|
|
}>(),
|
|
commentEditRequested: type<{ commentId: string; body: string }>(),
|
|
reactRequested: type<{ commentId: string; emoji: string; on: boolean }>(),
|
|
decisionRequested: type<{ commentId: string; on: boolean }>(),
|
|
threadResolveRequested: type<{
|
|
commentId: string;
|
|
resolved: boolean;
|
|
conclusion: string | null;
|
|
}>(),
|
|
commentToIssueRequested: type<{
|
|
commentId: string;
|
|
title: string;
|
|
type: string;
|
|
asSubtask: boolean;
|
|
}>(),
|
|
voteRequested: type<{ issueId: string; on: boolean }>(),
|
|
/** An image pasted into a text was uploaded (it joins the ticket's files). */
|
|
imagePasted: type<{ issueId: string }>(),
|
|
commentDeleteRequested: type<{ commentId: string }>(),
|
|
},
|
|
});
|
|
|
|
export const issueApiEvents = eventGroup({
|
|
source: 'Issue API',
|
|
events: {
|
|
loaded: type<Issue>(),
|
|
loadFailed: type<string>(),
|
|
commentsLoaded: type<Comment[]>(),
|
|
historyLoaded: type<Activity[]>(),
|
|
childrenLoaded: type<IssueSummary[]>(),
|
|
commandSucceeded: type<string>(),
|
|
/** A checklist step was saved (no toast for something so small). */
|
|
checklistSaved: type<void>(),
|
|
/** Changed, and «Deshacer» offered instead of a plain toast. */
|
|
undoOffered: type<void>(),
|
|
commandFailed: type<string>(),
|
|
},
|
|
});
|
|
|
|
export const issueRealtimeEvents = eventGroup({
|
|
source: 'Issue Realtime',
|
|
events: {
|
|
/** Something about this ticket changed: refetch it. */
|
|
issueTouched: type<{ issueId: string; version: number }>(),
|
|
/** A ticket under this one changed, arrived or left: reload them. */
|
|
childrenTouched: type<{ issueId: string }>(),
|
|
commentChanged: type<CommentChanged>(),
|
|
},
|
|
});
|