The board now filters by text, type, priority and unassigned tickets as well. Any combination can be saved as a named view, private or shared with the project, and opened again with one click. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
226 lines
7.4 KiB
TypeScript
226 lines
7.4 KiB
TypeScript
import { Observable } from 'rxjs';
|
|
import {
|
|
Activity,
|
|
Attachment,
|
|
BoardColumn,
|
|
Comment,
|
|
Epic,
|
|
SavedFilter,
|
|
SavedFilterCriteria,
|
|
CommandResult,
|
|
Issue,
|
|
IssuePage,
|
|
IssueSummary,
|
|
Notification,
|
|
NotificationSettings,
|
|
Project,
|
|
ProjectAnalytics,
|
|
ProjectStats,
|
|
Release,
|
|
ReleaseBurnup,
|
|
UserDirectoryEntry,
|
|
} from '../models/models';
|
|
import { ProjectSettings } from '../models/workflow';
|
|
import {
|
|
BoardFilter,
|
|
ChangeStatusInput,
|
|
CreateIssueInput,
|
|
CreateProjectInput,
|
|
CreateReleaseInput,
|
|
LinkIssuesInput,
|
|
MemberInput,
|
|
MyIssuesFilter,
|
|
UpdateIssueInput,
|
|
UpdateProjectInput,
|
|
} from '../models/inputs';
|
|
|
|
/*
|
|
* Outbound ports of the tracker. The application layer depends on these
|
|
* abstract classes; the infrastructure layer implements them over GraphQL.
|
|
*/
|
|
|
|
export abstract class ProjectsGateway {
|
|
abstract myProjects(): Observable<Project[]>;
|
|
/** Projects anyone can read (no sign-in needed). */
|
|
abstract publicProjects(): Observable<Project[]>;
|
|
abstract project(idOrKey: string): Observable<Project>;
|
|
abstract stats(projectId: string): Observable<ProjectStats>;
|
|
/** Statistics of the last `days` days. */
|
|
abstract analytics(
|
|
projectId: string,
|
|
days: number,
|
|
): Observable<ProjectAnalytics>;
|
|
/** Replaces statuses, transitions, types and areas (owners and admins). */
|
|
abstract updateSettings(
|
|
projectId: string,
|
|
settings: ProjectSettings,
|
|
): Observable<CommandResult>;
|
|
abstract create(input: CreateProjectInput): Observable<CommandResult>;
|
|
/** Name, description or visibility (owners and admins). */
|
|
abstract update(input: UpdateProjectInput): Observable<CommandResult>;
|
|
/** Owner only; `confirmKey` must be the project key. */
|
|
abstract delete(
|
|
projectId: string,
|
|
confirmKey: string,
|
|
): Observable<CommandResult>;
|
|
abstract addMember(input: MemberInput): Observable<CommandResult>;
|
|
abstract changeMemberRole(input: MemberInput): Observable<CommandResult>;
|
|
abstract removeMember(
|
|
projectId: string,
|
|
userId: string,
|
|
): Observable<CommandResult>;
|
|
}
|
|
|
|
export abstract class IssuesGateway {
|
|
abstract board(filter: BoardFilter): Observable<BoardColumn[]>;
|
|
abstract issue(idOrKey: string): Observable<Issue>;
|
|
abstract search(projectId: string, text: string): Observable<IssueSummary[]>;
|
|
/** Tickets across all my projects (most recently updated first). */
|
|
abstract mine(filter: MyIssuesFilter): Observable<IssuePage>;
|
|
abstract create(input: CreateIssueInput): Observable<CommandResult>;
|
|
abstract update(input: UpdateIssueInput): Observable<CommandResult>;
|
|
abstract changeStatus(input: ChangeStatusInput): Observable<CommandResult>;
|
|
abstract assign(
|
|
issueId: string,
|
|
assigneeId: string | null,
|
|
): Observable<CommandResult>;
|
|
abstract link(input: LinkIssuesInput): Observable<CommandResult>;
|
|
abstract unlink(input: LinkIssuesInput): Observable<CommandResult>;
|
|
abstract setRelease(
|
|
issueId: string,
|
|
releaseId: string | null,
|
|
): Observable<CommandResult>;
|
|
/** The signed-in person follows (or stops following) the ticket. */
|
|
abstract watch(issueId: string, watching: boolean): Observable<CommandResult>;
|
|
/** Tickets hanging from `issueId`. */
|
|
abstract children(
|
|
projectId: string,
|
|
issueId: string,
|
|
): Observable<IssueSummary[]>;
|
|
/** Places the ticket under `parentId` (null: back to the top). */
|
|
abstract setParent(
|
|
issueId: string,
|
|
parentId: string | null,
|
|
): Observable<CommandResult>;
|
|
abstract epics(projectId: string): Observable<Epic[]>;
|
|
/** Tickets still to start (TODO category), in backlog order. */
|
|
abstract backlog(projectId: string): Observable<IssuePage>;
|
|
/** Moves a ticket between two others of the backlog (null: an end). */
|
|
abstract rank(
|
|
issueId: string,
|
|
aboveIssueId: string | null,
|
|
belowIssueId: string | null,
|
|
): Observable<CommandResult>;
|
|
abstract addChecklistItem(
|
|
issueId: string,
|
|
text: string,
|
|
): Observable<CommandResult>;
|
|
abstract updateChecklistItem(
|
|
issueId: string,
|
|
itemId: string,
|
|
changes: { text?: string; done?: boolean },
|
|
): Observable<CommandResult>;
|
|
abstract removeChecklistItem(
|
|
issueId: string,
|
|
itemId: string,
|
|
): Observable<CommandResult>;
|
|
}
|
|
|
|
/** Saved views of a project's board (mine and the shared ones). */
|
|
export abstract class SavedFiltersGateway {
|
|
abstract list(projectId: string): Observable<SavedFilter[]>;
|
|
abstract save(input: {
|
|
projectId: string;
|
|
name: string;
|
|
shared: boolean;
|
|
criteria: SavedFilterCriteria;
|
|
}): Observable<string>;
|
|
abstract remove(id: string): Observable<void>;
|
|
}
|
|
|
|
export abstract class CommentsGateway {
|
|
abstract list(issueId: string): Observable<Comment[]>;
|
|
abstract add(issueId: string, body: string): Observable<CommandResult>;
|
|
abstract edit(commentId: string, body: string): Observable<CommandResult>;
|
|
abstract remove(commentId: string): Observable<CommandResult>;
|
|
}
|
|
|
|
/** A ticket's files. Uploads and downloads go through the REST API. */
|
|
export abstract class AttachmentsGateway {
|
|
abstract upload(
|
|
issueId: string,
|
|
file: File,
|
|
commentId?: string | null,
|
|
): Observable<CommandResult>;
|
|
/** The file's bytes, to show (images) or save. */
|
|
abstract download(issueId: string, attachment: Attachment): Observable<Blob>;
|
|
abstract remove(
|
|
issueId: string,
|
|
attachmentId: string,
|
|
): Observable<CommandResult>;
|
|
}
|
|
|
|
export abstract class ReleasesGateway {
|
|
abstract list(projectId: string): Observable<Release[]>;
|
|
/** Planned releases of all my projects, soonest target date first. */
|
|
abstract upcoming(limit: number): Observable<Release[]>;
|
|
abstract create(input: CreateReleaseInput): Observable<CommandResult>;
|
|
abstract publish(
|
|
releaseId: string,
|
|
force: boolean,
|
|
): Observable<CommandResult>;
|
|
abstract archive(releaseId: string): Observable<CommandResult>;
|
|
abstract burnup(releaseId: string): Observable<ReleaseBurnup>;
|
|
}
|
|
|
|
export abstract class ActivityGateway {
|
|
abstract list(projectId: string, issueId?: string): Observable<Activity[]>;
|
|
}
|
|
|
|
export abstract class UsersGateway {
|
|
abstract search(term: string): Observable<UserDirectoryEntry[]>;
|
|
}
|
|
|
|
/** The signed-in person's notifications. */
|
|
export abstract class NotificationsGateway {
|
|
abstract mine(
|
|
unreadOnly: boolean,
|
|
limit: number,
|
|
): Observable<{ items: Notification[]; unread: number }>;
|
|
/** Without ids: all of them. */
|
|
abstract markRead(ids?: string[]): Observable<void>;
|
|
abstract settings(): Observable<NotificationSettings>;
|
|
abstract updateSettings(email: boolean): Observable<NotificationSettings>;
|
|
}
|
|
|
|
/** Realtime (GraphQL subscriptions over WebSocket). */
|
|
export interface IssueChanged {
|
|
action: string;
|
|
version: number;
|
|
actorId: string;
|
|
issue: IssueSummary;
|
|
}
|
|
|
|
export interface CommentChanged {
|
|
action: string;
|
|
commentId: string;
|
|
comment: Comment | null;
|
|
}
|
|
|
|
/** A project changed, or was deleted (then `project` is its last state). */
|
|
export interface ProjectChanged {
|
|
project: Project;
|
|
deleted: boolean;
|
|
}
|
|
|
|
/** Realtime needs a session: without one, these streams stay silent. */
|
|
export abstract class TrackerRealtimeGateway {
|
|
abstract issueChanges(projectId: string): Observable<IssueChanged>;
|
|
abstract commentChanges(issueId: string): Observable<CommentChanged>;
|
|
abstract activity(projectId: string): Observable<Activity>;
|
|
abstract releaseChanges(projectId: string): Observable<Release>;
|
|
abstract projectChanges(projectId: string): Observable<ProjectChanged>;
|
|
abstract myProjectsChanges(): Observable<ProjectChanged>;
|
|
abstract notifications(): Observable<Notification>;
|
|
}
|