src/app/issue-detail/issue-contents.component.ts
This component renders the contents of the issue: the issue body, timeline, and comment box.
selector | app-issue-contents |
styleUrls | issue-contents.component.scss |
templateUrl | issue-contents.component.html |
Properties |
|
Methods |
|
Inputs |
constructor(dataService: DataService)
|
||||||
Parameters :
|
issue$ | |
Type : DataNode<Issue>
|
|
The issue to be rendered. |
projectId | |
Type : string
|
|
The raw project ID. |
Public closeIssue |
closeIssue()
|
Closes the current issue.
Returns :
void
|
Public commentIssue |
commentIssue()
|
Adds a comment to the current issue with the data provided in the comment box.
Returns :
void
|
Public reopenIssue |
reopenIssue()
|
Reopens the currently closed issue.
Returns :
void
|
commentEditor |
Decorators :
@ViewChild('comment')
|
The comment editor (app-markdown-editor). |
Public savingComment |
Default value : false
|
True if the issue comment is currently being saved. |
import {Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {DataList, DataNode} from '@app/data-dgql/query';
import {Issue} from '../../generated/graphql';
import {Subscription} from 'rxjs';
import DataService from '@app/data-dgql';
import {CURRENT_USER_NODE, ListType} from '@app/data-dgql/id';
import {User} from '../../generated/graphql-dgql';
/**
* This component renders the contents of the issue: the issue body, timeline, and comment box.
*/
@Component({
selector: 'app-issue-contents',
templateUrl: 'issue-contents.component.html',
styleUrls: ['issue-contents.component.scss']
})
export class IssueContentsComponent implements OnInit, OnDestroy {
/** The issue to be rendered. */
@Input() issue$: DataNode<Issue>;
/** The raw project ID. */
@Input() projectId: string;
/** @ignore */
public linkedIssues$: DataList<Issue, unknown>;
/** @ignore */
public linkedIssueSub: Subscription;
/** @ignore */
public currentUser$: DataNode<User>;
/** @ignore */
public currentUserSub: Subscription;
/** True if the issue comment is currently being saved. */
public savingComment = false;
/** The comment editor (app-markdown-editor). */
@ViewChild('comment') commentEditor;
constructor(private dataService: DataService) {}
ngOnInit() {
this.linkedIssues$ = this.dataService.getList({
node: this.issue$.id,
type: ListType.LinkedIssues
});
this.linkedIssueSub = this.linkedIssues$.subscribe();
this.currentUser$ = this.dataService.getNode(CURRENT_USER_NODE);
this.currentUserSub = this.currentUser$.subscribe();
}
ngOnDestroy() {
this.linkedIssueSub.unsubscribe();
this.currentUserSub.unsubscribe();
}
/** Closes the current issue. */
public closeIssue(): void {
this.dataService.mutations.closeIssue(Math.random().toString(), this.issue$.id);
}
/** Reopens the currently closed issue. */
public reopenIssue(): void {
this.dataService.mutations.reopenIssue(Math.random().toString(), this.issue$.id);
}
/** Adds a comment to the current issue with the data provided in the comment box. */
public commentIssue(): void {
this.savingComment = true;
this.dataService.mutations
.addIssueComment(Math.random().toString(), this.issue$.id, this.commentEditor.code)
.then(() => {
// only clear if successful
this.commentEditor.code = '';
})
.finally(() => {
this.savingComment = false;
});
}
}
<div class="issue-contents" *ngIf="issue$?.current as issue">
<!-- Issue body -->
<app-comment class="issue-body" [commentId]="issue$.id" [isIssueBody]="true"></app-comment>
<!-- Linked issues -->
<div *ngIf="linkedIssues$.totalCount > 0" class="timeline-item">
<div class="item-box">
<div class="item-header">
<mat-icon>link</mat-icon>
<h3 class="item-title">
<b>Linked Issues</b>
</h3>
</div>
<div class="item-body">
<div class="linked-issues-container">
<mat-accordion>
<ng-container *ngFor="let currentIssue of linkedIssues$.currentItems">
<app-issue-detail-linked-issue-item [issueStub]="currentIssue" [projectId]="projectId"> </app-issue-detail-linked-issue-item>
</ng-container>
</mat-accordion>
</div>
<app-cursor-paginator *ngIf="linkedIssues$.totalCount > 10" [list]="linkedIssues$" [pageSizes]="[10]"></app-cursor-paginator>
</div>
</div>
</div>
<app-timeline [issueId]="issue$.id" [projectID]="projectId"></app-timeline>
<mat-divider></mat-divider>
<!-- Comment creation box-->
<div class="timeline-item comment-form">
<div class="item-box">
<div class="item-header">
<h3 class="item-title">
Comment this issue as
<app-user-item [user]="currentUser$.current" [short]="true"></app-user-item>
</h3>
</div>
<div class="item-body">
<app-markdown-editor #comment></app-markdown-editor>
<div class="comment-buttons">
<button *ngIf="issue.isOpen" mat-raised-button color="warn" (click)="this.closeIssue()">Close Issue</button>
<button *ngIf="!issue.isOpen" mat-raised-button color="accent" (click)="this.reopenIssue()">Reopen Issue</button>
<button mat-raised-button color="primary" style="margin-left: 6px" [class.spinner]="savingComment" (click)="this.commentIssue()">
Comment
</button>
</div>
</div>
</div>
</div>
</div>
issue-contents.component.scss
@import "src/styles/spinner";
.timeline-item {
position: relative;
margin-left: 36px;
padding: 12px 0;
&:first-child {
padding-top: 0;
}
&::before {
content: "";
position: absolute;
top: 0;
left: -1px;
bottom: 0;
width: 2px;
background: rgba(0, 0, 0, 0.12);
}
.item-box {
position: relative;
background: white;
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.1);
margin-left: -36px;
.item-header {
background: rgba(0, 0, 0, 0.06);
border-bottom: rgba(0, 0, 0, 0.1);
border-radius: 4px 4px 0 0;
padding: 8px 16px;
display: flex;
align-items: center;
mat-icon {
margin-right: 8px;
margin-left: -8px;
}
.item-title {
flex: 1;
margin: 0 16px 0 0;
overflow: hidden;
font-size: inherit;
font-weight: inherit;
.item-edited {
opacity: 0.5;
}
}
}
.item-body {
padding: 16px;
&.is-editing {
padding: 8px;
}
.edit-body-buttons {
text-align: right;
margin-top: 5px;
margin-bottom: 3px;
}
}
}
}
.comment-form {
padding-bottom: 0;
.comment-buttons {
text-align: right;
}
}