]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/+videos/+video-watch/comment/video-comment.component.ts
Instance homepage support (#4007)
[github/Chocobozzz/PeerTube.git] / client / src / app / +videos / +video-watch / comment / video-comment.component.ts
CommitLineData
8ca56654
C
1
2import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core'
67ed6552 3import { MarkdownService, Notifier, UserService } from '@app/core'
edf1a4e5 4import { AuthService } from '@app/core/auth'
c418d483 5import { Account, DropdownAction, Video } from '@app/shared/shared-main'
cfde28ba
C
6import { CommentReportComponent } from '@app/shared/shared-moderation/report-modals/comment-report.component'
7import { VideoComment, VideoCommentThreadTree } from '@app/shared/shared-video-comment'
67ed6552 8import { User, UserRight } from '@shared/models'
4635f59d
C
9
10@Component({
11 selector: 'my-video-comment',
12 templateUrl: './video-comment.component.html',
13 styleUrls: ['./video-comment.component.scss']
14})
1263fc4e 15export class VideoCommentComponent implements OnInit, OnChanges {
8ca56654
C
16 @ViewChild('commentReportModal') commentReportModal: CommentReportComponent
17
4635f59d
C
18 @Input() video: Video
19 @Input() comment: VideoComment
d7e70384 20 @Input() parentComments: VideoComment[] = []
4635f59d
C
21 @Input() commentTree: VideoCommentThreadTree
22 @Input() inReplyToCommentId: number
1263fc4e 23 @Input() highlightedComment = false
c62a34d3 24 @Input() firstInThread = false
f63c03fb 25 @Input() redraftValue?: string
4635f59d
C
26
27 @Output() wantedToReply = new EventEmitter<VideoComment>()
f63c03fb 28 @Output() wantedToDelete = new EventEmitter<VideoComment>()
29 @Output() wantedToRedraft = new EventEmitter<VideoComment>()
4cb6d457 30 @Output() threadCreated = new EventEmitter<VideoCommentThreadTree>()
4635f59d 31 @Output() resetReply = new EventEmitter()
b29bf61d 32 @Output() timestampClicked = new EventEmitter<number>()
4635f59d 33
8ca56654
C
34 prependModerationActions: DropdownAction<any>[]
35
2890b615 36 sanitizedCommentHTML = ''
c199c427 37 newParentComments: VideoComment[] = []
2890b615 38
edf1a4e5
RK
39 commentAccount: Account
40 commentUser: User
41
3d9eaae3 42 constructor (
1aa75434 43 private markdownService: MarkdownService,
edf1a4e5 44 private authService: AuthService,
edf1a4e5
RK
45 private userService: UserService,
46 private notifier: Notifier
3d9eaae3 47 ) {}
cf117aaa
C
48
49 get user () {
50 return this.authService.getUser()
4635f59d
C
51 }
52
2890b615 53 ngOnInit () {
1263fc4e
C
54 this.init()
55 }
d7e70384 56
1263fc4e
C
57 ngOnChanges () {
58 this.init()
2890b615
C
59 }
60
ae45f988
C
61 onCommentReplyCreated (createdComment: VideoComment) {
62 if (!this.commentTree) {
63 this.commentTree = {
64 comment: this.comment,
9d6b9d10 65 hasDisplayedChildren: false,
ae45f988
C
66 children: []
67 }
4cb6d457
C
68
69 this.threadCreated.emit(this.commentTree)
ae45f988
C
70 }
71
86ec3e53 72 this.commentTree.children.unshift({
ae45f988 73 comment: createdComment,
9d6b9d10 74 hasDisplayedChildren: false,
ae45f988
C
75 children: []
76 })
f63c03fb 77
ae45f988 78 this.resetReply.emit()
f63c03fb 79
fdd12965 80 this.redraftValue = undefined
4635f59d
C
81 }
82
4cb6d457
C
83 onWantToReply (comment?: VideoComment) {
84 this.wantedToReply.emit(comment || this.comment)
4635f59d
C
85 }
86
4cb6d457
C
87 onWantToDelete (comment?: VideoComment) {
88 this.wantedToDelete.emit(comment || this.comment)
4635f59d
C
89 }
90
f63c03fb 91 onWantToRedraft (comment?: VideoComment) {
92 this.wantedToRedraft.emit(comment || this.comment)
93 }
94
4cb6d457
C
95 isUserLoggedIn () {
96 return this.authService.isLoggedIn()
4635f59d
C
97 }
98
99 onResetReply () {
100 this.resetReply.emit()
101 }
cf117aaa 102
b29bf61d
RK
103 handleTimestampClicked (timestamp: number) {
104 this.timestampClicked.emit(timestamp)
105 }
106
4cb6d457 107 isRemovableByUser () {
b29bf61d 108 return this.comment.account && this.isUserLoggedIn() &&
4cb6d457
C
109 (
110 this.user.account.id === this.comment.account.id ||
fde37dc9 111 this.user.account.id === this.video.account.id ||
4cb6d457
C
112 this.user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)
113 )
114 }
1263fc4e 115
f63c03fb 116 isRedraftableByUser () {
ddb0303f 117 return (
118 this.comment.account &&
119 this.isUserLoggedIn() &&
120 this.user.account.id === this.comment.account.id &&
121 this.comment.totalReplies === 0
122 )
f63c03fb 123 }
124
ddb0303f 125 isReportableByUser () {
126 return (
127 this.comment.account &&
128 this.isUserLoggedIn() &&
129 this.comment.isDeleted === false &&
230c3ba2 130 this.user.account.id !== this.comment.account.id
ddb0303f 131 )
45ae994a 132 }
133
9d6b9d10
C
134 isCommentDisplayed () {
135 // Not deleted
136 return !this.comment.isDeleted ||
137 this.comment.totalReplies !== 0 || // Or root comment thread has replies
138 (this.commentTree?.hasDisplayedChildren) // Or this is a reply that have other replies
fdd12965 139 }
140
746018f6
C
141 isChild () {
142 return this.parentComments.length !== 0
143 }
144
edf1a4e5
RK
145 private getUserIfNeeded (account: Account) {
146 if (!account.userId) return
147 if (!this.authService.isLoggedIn()) return
148
149 const user = this.authService.getUser()
150 if (user.hasRight(UserRight.MANAGE_USERS)) {
218b0874 151 this.userService.getUserWithCache(account.userId)
edf1a4e5
RK
152 .subscribe(
153 user => this.commentUser = user,
154
155 err => this.notifier.error(err.message)
156 )
157 }
158 }
159
41d71344 160 private async init () {
110d463f 161 // Before HTML rendering restore line feed for markdown list compatibility
162 const commentText = this.comment.text.replace(/<br.?\/?>/g, '\r\n')
163 const html = await this.markdownService.textMarkdownToHTML(commentText, true, true)
2539932e 164 this.sanitizedCommentHTML = this.markdownService.processVideoTimestamps(html)
1263fc4e 165 this.newParentComments = this.parentComments.concat([ this.comment ])
6cb55644
C
166
167 if (this.comment.account) {
168 this.commentAccount = new Account(this.comment.account)
169 this.getUserIfNeeded(this.commentAccount)
170 } else {
171 this.comment.account = null
172 }
8ca56654 173
45ae994a 174 this.prependModerationActions = []
175
176 if (this.isReportableByUser()) {
177 this.prependModerationActions.push({
d3bb7994 178 label: $localize`Report this comment`,
c3115960 179 iconName: 'flag',
45ae994a 180 handler: () => this.showReportModal()
181 })
182 }
183
184 if (this.isRemovableByUser()) {
185 this.prependModerationActions.push({
c3115960 186 label: $localize`Remove`,
187 iconName: 'delete',
45ae994a 188 handler: () => this.onWantToDelete()
189 })
190 }
191
192 if (this.isRedraftableByUser()) {
193 this.prependModerationActions.push({
c3115960 194 label: $localize`Remove & re-draft`,
195 iconName: 'edit',
45ae994a 196 handler: () => this.onWantToRedraft()
197 })
198 }
199
200 if (this.prependModerationActions.length === 0) {
8ca56654
C
201 this.prependModerationActions = undefined
202 }
203 }
204
205 private showReportModal () {
206 this.commentReportModal.show()
1263fc4e 207 }
4635f59d 208}