src/app/issue-detail/comment/comment.component.ts
This component displays an issue comment. The comment will be subscribed to lazily (see DataNode).
selector | app-comment |
styleUrls | ./comment.component.scss |
templateUrl | ./comment.component.html |
Properties |
|
Methods |
|
Inputs |
constructor(dataService: DataService, dialog: MatDialog, notify: UserNotifyService)
|
||||||||||||
Parameters :
|
commentId | |
Type : NodeId
|
|
The comment or issue ID. If this is an issue ID, set to true. |
isIssueBody | |
Type : boolean
|
|
If true, this comment component is actually editing an issue's body. |
issueId | |
Type : NodeId
|
|
The issue this comment belongs to. |
Public deleteComment |
deleteComment()
|
Deletes the current comment.
Returns :
void
|
Public editComment | ||||||||
editComment(body: string)
|
||||||||
Edits the description of the current comment.
Parameters :
Returns :
void
|
Public editBody |
Default value : false
|
True if the comment body is being edited. |
Public savingBody |
Default value : false
|
True if the comment body is being saved. |
Public timeFormatter |
Default value : new TimeFormatter()
|
Used to format time. |
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
import {IssueComment} from '../../../generated/graphql-dgql';
import {TimeFormatter} from '@app/issue-detail/time-formatter';
import DataService from '@app/data-dgql';
import {NodeId} from '@app/data-dgql/id';
import {DataNode} from '@app/data-dgql/query';
import {Subscription} from 'rxjs';
import {RemoveDialogComponent} from '@app/dialogs/remove-dialog/remove-dialog.component';
import {MatDialog} from '@angular/material/dialog';
import {UserNotifyService} from '@app/user-notify/user-notify.service';
/**
* This component displays an issue comment.
* The comment will be subscribed to lazily (see {@link DataNode#subscribeLazy}).
*/
@Component({
selector: 'app-comment',
templateUrl: './comment.component.html',
styleUrls: ['./comment.component.scss']
})
export class CommentComponent implements OnInit, OnDestroy {
/** If true, this comment component is actually editing an issue's body. */
@Input() isIssueBody: boolean;
/** The comment or issue ID. If this is an issue ID, set {@link #isIssueBody} to true. */
@Input() commentId: NodeId;
/** The issue this comment belongs to. */
@Input() issueId: NodeId;
/** Used to format time. */
public timeFormatter = new TimeFormatter();
/** True if the comment body is being edited. */
public editBody = false;
/** True if the comment body is being saved. */
public savingBody = false;
/**
* @ignore
* Internal: comment data node view.
*/
comment$: DataNode<IssueComment>;
/**
* @ignore
* Internal: subscription to comment$.
*/
commentSub: Subscription;
constructor(private dataService: DataService, private dialog: MatDialog, private notify: UserNotifyService) {}
ngOnInit() {
this.comment$ = this.dataService.getNode(this.commentId);
this.commentSub = this.comment$.subscribeLazy();
}
ngOnDestroy() {
this.commentSub?.unsubscribe();
}
/**
* Edits the description of the current comment.
*
* @param body - The new description of the current issue or comment.
*/
public editComment(body: string): void {
this.savingBody = true;
this.dataService.mutations
.updateIssueComment(Math.random().toString(), this.commentId, body)
.then(() => {
// only exit if successful
this.editBody = false;
})
.finally(() => {
this.savingBody = false;
});
}
/**
* Deletes the current comment.
*/
public deleteComment(): void {
const confirmDeleteDialogRef = this.dialog.open(RemoveDialogComponent, {
data: {
title: 'Really delete comment ?',
messages: ['Are you sure you want to delete this comment ?', 'This action cannot be undone!']
}
});
confirmDeleteDialogRef.afterClosed().subscribe(
(del) => {
if (del) {
// User confirmed deletion
this.dataService.mutations.deleteIssueComment(Math.random().toString(), this.issueId, this.commentId).then(() => {
this.notify.notifyInfo('Successfully deleted comment');
});
}
},
(error) => this.notify.notifyError('Failed to delete project!', error)
);
}
}
<div class="timeline-item">
<div class="item-box" *ngIf="comment$.hasData && comment$.current as comment">
<div class="item-header">
<h3 class="item-title">
<!-- Comment title, with user name and creation time -->
<app-user-item [short]="true" [user]="comment.createdBy"></app-user-item>
commented
<time [dateTime]="comment.createdAt" [title]="this.timeFormatter.formatTime(comment.createdAt)">
{{ this.timeFormatter.formatTimeDifference(comment.createdAt) }}
</time>
<span class="item-edited" *ngIf="comment.lastEditedAt !== comment.createdAt">
⋅<app-user-item [short]="true" [user]="comment.editedBy?.nodes[0]"></app-user-item>
edited
<time [dateTime]="comment.lastEditedAt" [title]="this.timeFormatter.formatTime(comment.lastEditedAt)">
{{ this.timeFormatter.formatTimeDifference(comment.lastEditedAt) }}
</time>
</span>
</h3>
<!-- Edit and delete comment buttons -->
<!-- Delete button is only for comments and not for issue description displayed -->
<button *ngIf="!this.editBody && !this.isIssueBody" class="item-edit" color="warn" mat-flat-button (click)="this.deleteComment()">
Delete
</button>
<button
*ngIf="!this.editBody"
class="item-edit"
style="margin-left: 6px"
color="primary"
mat-flat-button
(click)="this.editBody = !this.editBody"
>
Edit
</button>
</div>
<!-- Comment body -->
<div *ngIf="!this.editBody" class="item-body">
<app-markdown-preview [displayedCode]="comment.body"></app-markdown-preview>
</div>
<!-- Comment body during editing -->
<div *ngIf="this.editBody" class="item-body is-editing" [ngStyle]="{ height: this.editBody ? '280px' : 'auto' }">
<app-markdown-editor #bodyEdit [code]="comment.body"></app-markdown-editor>
<!-- Save/Cancel buttons -->
<div class="edit-body-buttons">
<button mat-raised-button (click)="this.editBody = !this.editBody">Cancel</button>
<button mat-raised-button style="margin-left: 6px" color="accent" [class.spinner]="savingBody" (click)="editComment(bodyEdit.code)">
Save
</button>
</div>
</div>
</div>
</div>
./comment.component.scss
@import "../issue-contents.component";