feat: what is new since my last visit, and turning a comment into a ticket
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>
This commit is contained in:
@@ -25,6 +25,7 @@ import {
|
||||
ReactToCommentDocument,
|
||||
MarkDecisionDocument,
|
||||
ResolveThreadDocument,
|
||||
TurnCommentIntoIssueDocument,
|
||||
VotePollDocument,
|
||||
ClosePollDocument,
|
||||
PublishReleaseDocument,
|
||||
@@ -111,6 +112,22 @@ export class GraphqlCommentsGateway extends CommentsGateway {
|
||||
.pipe(map((d) => toCommandResult(d.resolveThread)));
|
||||
}
|
||||
|
||||
turnIntoIssue(
|
||||
commentId: string,
|
||||
title: string,
|
||||
type: string,
|
||||
asSubtask: boolean,
|
||||
): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(TurnCommentIntoIssueDocument, {
|
||||
commentId,
|
||||
title,
|
||||
type,
|
||||
asSubtask,
|
||||
})
|
||||
.pipe(map((d) => toCommandResult(d.turnCommentIntoIssue)));
|
||||
}
|
||||
|
||||
markDecision(commentId: string, on: boolean): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(MarkDecisionDocument, { commentId, on })
|
||||
|
||||
@@ -57,6 +57,7 @@ import {
|
||||
CancelReviewDocument,
|
||||
MyPinsDocument,
|
||||
FollowTreeDocument,
|
||||
VisitIssueDocument,
|
||||
SetReminderDocument,
|
||||
MyDayDocument,
|
||||
AddToMyDayDocument,
|
||||
@@ -272,6 +273,12 @@ export class GraphqlIssuesGateway extends IssuesGateway {
|
||||
.pipe(map(() => undefined));
|
||||
}
|
||||
|
||||
visit(issueId: string): Observable<string | null> {
|
||||
return this.gql
|
||||
.mutate(VisitIssueDocument, { issueId })
|
||||
.pipe(map((d) => d.visitIssue ?? null));
|
||||
}
|
||||
|
||||
followTree(issueId: string, on: boolean): Observable<void> {
|
||||
const done$: Observable<unknown> = on
|
||||
? this.gql.mutate(FollowTreeDocument, { issueId })
|
||||
|
||||
@@ -218,6 +218,10 @@ fragment CommentFields on Comment {
|
||||
resolvedBy {
|
||||
...UserRefFields
|
||||
}
|
||||
spawned {
|
||||
id
|
||||
key
|
||||
}
|
||||
createdAt
|
||||
editedAt
|
||||
author {
|
||||
|
||||
@@ -754,6 +754,7 @@ export type IssueFieldsFragment = {
|
||||
}>;
|
||||
} | null;
|
||||
resolvedBy: { id: string; name: string } | null;
|
||||
spawned: Array<{ id: string; key: string }>;
|
||||
author: { id: string; name: string };
|
||||
}>;
|
||||
reporter: { id: string; name: string };
|
||||
@@ -817,6 +818,7 @@ export type CommentFieldsFragment = {
|
||||
}>;
|
||||
} | null;
|
||||
resolvedBy: { id: string; name: string } | null;
|
||||
spawned: Array<{ id: string; key: string }>;
|
||||
author: { id: string; name: string };
|
||||
};
|
||||
|
||||
@@ -1027,6 +1029,7 @@ export type IssueQuery = {
|
||||
}>;
|
||||
} | null;
|
||||
resolvedBy: { id: string; name: string } | null;
|
||||
spawned: Array<{ id: string; key: string }>;
|
||||
author: { id: string; name: string };
|
||||
}>;
|
||||
reporter: { id: string; name: string };
|
||||
@@ -1398,6 +1401,7 @@ export type IssueCommentsQuery = {
|
||||
}>;
|
||||
} | null;
|
||||
resolvedBy: { id: string; name: string } | null;
|
||||
spawned: Array<{ id: string; key: string }>;
|
||||
author: { id: string; name: string };
|
||||
}>;
|
||||
};
|
||||
@@ -1830,6 +1834,23 @@ export type ResolveThreadMutation = {
|
||||
resolveThread: { id: string; key: string | null; version: number };
|
||||
};
|
||||
|
||||
export type TurnCommentIntoIssueMutationVariables = Exact<{
|
||||
commentId: string;
|
||||
title: string;
|
||||
type: string;
|
||||
asSubtask: boolean;
|
||||
}>;
|
||||
|
||||
export type TurnCommentIntoIssueMutation = {
|
||||
turnCommentIntoIssue: { id: string; key: string | null; version: number };
|
||||
};
|
||||
|
||||
export type VisitIssueMutationVariables = Exact<{
|
||||
issueId: string;
|
||||
}>;
|
||||
|
||||
export type VisitIssueMutation = { visitIssue: string | null };
|
||||
|
||||
export type MarkDecisionMutationVariables = Exact<{
|
||||
commentId: string;
|
||||
on: boolean;
|
||||
@@ -1870,6 +1891,7 @@ export type SearchDecisionsQuery = {
|
||||
}>;
|
||||
} | null;
|
||||
resolvedBy: { id: string; name: string } | null;
|
||||
spawned: Array<{ id: string; key: string }>;
|
||||
author: { id: string; name: string };
|
||||
};
|
||||
issue: { id: string; key: string; title: string };
|
||||
@@ -3254,6 +3276,7 @@ export type CommentChangedSubscription = {
|
||||
}>;
|
||||
} | null;
|
||||
resolvedBy: { id: string; name: string } | null;
|
||||
spawned: Array<{ id: string; key: string }>;
|
||||
author: { id: string; name: string };
|
||||
} | null;
|
||||
};
|
||||
@@ -4091,6 +4114,17 @@ export const CommentFieldsFragmentDoc = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'spawned' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'key' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'createdAt' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'editedAt' } },
|
||||
{
|
||||
@@ -4536,6 +4570,17 @@ export const IssueFieldsFragmentDoc = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'spawned' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'key' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'createdAt' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'editedAt' } },
|
||||
{
|
||||
@@ -6853,6 +6898,17 @@ export const IssueDocument = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'spawned' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'key' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'createdAt' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'editedAt' } },
|
||||
{
|
||||
@@ -9204,6 +9260,17 @@ export const IssueCommentsDocument = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'spawned' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'key' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'createdAt' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'editedAt' } },
|
||||
{
|
||||
@@ -12014,6 +12081,181 @@ export const ResolveThreadDocument = {
|
||||
ResolveThreadMutation,
|
||||
ResolveThreadMutationVariables
|
||||
>;
|
||||
export const TurnCommentIntoIssueDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
{
|
||||
kind: 'OperationDefinition',
|
||||
operation: 'mutation',
|
||||
name: { kind: 'Name', value: 'TurnCommentIntoIssue' },
|
||||
variableDefinitions: [
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'commentId' },
|
||||
},
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'title' },
|
||||
},
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: {
|
||||
kind: 'NamedType',
|
||||
name: { kind: 'Name', value: 'String' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: { kind: 'Variable', name: { kind: 'Name', value: 'type' } },
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: {
|
||||
kind: 'NamedType',
|
||||
name: { kind: 'Name', value: 'String' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'asSubtask' },
|
||||
},
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: {
|
||||
kind: 'NamedType',
|
||||
name: { kind: 'Name', value: 'Boolean' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'turnCommentIntoIssue' },
|
||||
arguments: [
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'commentId' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'commentId' },
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'title' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'title' },
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'type' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'type' },
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'asSubtask' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'asSubtask' },
|
||||
},
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{
|
||||
kind: 'FragmentSpread',
|
||||
name: { kind: 'Name', value: 'CommandResultFields' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'FragmentDefinition',
|
||||
name: { kind: 'Name', value: 'CommandResultFields' },
|
||||
typeCondition: {
|
||||
kind: 'NamedType',
|
||||
name: { kind: 'Name', value: 'CommandResult' },
|
||||
},
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'key' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'version' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
} as unknown as DocumentNode<
|
||||
TurnCommentIntoIssueMutation,
|
||||
TurnCommentIntoIssueMutationVariables
|
||||
>;
|
||||
export const VisitIssueDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
{
|
||||
kind: 'OperationDefinition',
|
||||
operation: 'mutation',
|
||||
name: { kind: 'Name', value: 'VisitIssue' },
|
||||
variableDefinitions: [
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'issueId' },
|
||||
},
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
|
||||
},
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'visitIssue' },
|
||||
arguments: [
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'issueId' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'issueId' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
} as unknown as DocumentNode<VisitIssueMutation, VisitIssueMutationVariables>;
|
||||
export const MarkDecisionDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
@@ -12304,6 +12546,17 @@ export const SearchDecisionsDocument = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'spawned' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'key' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'createdAt' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'editedAt' } },
|
||||
{
|
||||
@@ -19548,6 +19801,17 @@ export const CommentChangedDocument = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'spawned' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'key' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'createdAt' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'editedAt' } },
|
||||
{
|
||||
|
||||
@@ -396,6 +396,26 @@ mutation ResolveThread(
|
||||
}
|
||||
}
|
||||
|
||||
mutation TurnCommentIntoIssue(
|
||||
$commentId: ID!
|
||||
$title: String!
|
||||
$type: String!
|
||||
$asSubtask: Boolean!
|
||||
) {
|
||||
turnCommentIntoIssue(
|
||||
commentId: $commentId
|
||||
title: $title
|
||||
type: $type
|
||||
asSubtask: $asSubtask
|
||||
) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation VisitIssue($issueId: ID!) {
|
||||
visitIssue(issueId: $issueId)
|
||||
}
|
||||
|
||||
mutation MarkDecision($commentId: ID!, $on: Boolean!) {
|
||||
markDecision(commentId: $commentId, on: $on) {
|
||||
...CommandResultFields
|
||||
|
||||
Reference in New Issue
Block a user